mirror of
https://github.com/NeteaseCloudMusicApiEnhanced/api-enhanced.git
synced 2025-10-22 06:03:09 +00:00
feat(logger}: 支持更好的日志输出, 完善了文档
This commit is contained in:
parent
11e1c62cc3
commit
ceb2044500
@ -1,4 +1,7 @@
|
||||
# 更新日志
|
||||
|
||||
## 二开作者注: 这些`commit`记录为原版网易云音乐API的记录, 本项目不会对其进行添加以及修改
|
||||
|
||||
### 4.25.0 | 2024.11.16
|
||||
- feat: 增加副歌时间、相关歌单推荐接口,原有相关歌单接口已废弃;fix: 将部分易盾白名单接口替换为eapi [#30](https://gitlab.com/Binaryify/neteasecloudmusicapi/-/merge_requests/30)
|
||||
- fix: 播客上传接口 [#32](https://gitlab.com/Binaryify/neteasecloudmusicapi/-/merge_requests/32)
|
||||
|
@ -1,4 +1,4 @@
|
||||
# 网易云音乐 API Enhanced(Reborn)
|
||||
# 网易云音乐 API Enhanced
|
||||
|
||||
---
|
||||
|
||||
@ -34,16 +34,18 @@ set PORT=4000 && node app.js # Windows
|
||||
|
||||
### 重要提示
|
||||
|
||||
- 调用前请务必阅读文档的“调用前须知”部分。
|
||||
- 调用前请务必阅读文档的 [调用前须知](https://music-api.focalors.ltd/#/?id=%e8%b0%83%e7%94%a8%e5%89%8d%e9%a1%bb%e7%9f%a5) 部分。
|
||||
- 推荐将敏感信息(如 cookie)通过部署平台的环境变量进行配置。
|
||||
|
||||
## 在线体验与文档
|
||||
|
||||
- [在线文档](https://music-api.focalors.ltd/docs/#)
|
||||
- [在线文档](https://music-api.focalors.ltd/)
|
||||
- [NPM 包地址](https://www.npmjs.com/package/@neteaseapireborn/api)
|
||||
|
||||
## 常见部署方式
|
||||
|
||||
- 推荐参考[在线文档](https://music-api.focalors.ltd/)
|
||||
|
||||
### Vercel 一键部署
|
||||
|
||||
1. fork 本项目
|
||||
@ -114,4 +116,3 @@ pnpm test
|
||||
## License
|
||||
|
||||
[MIT License](https://github.com/IamFurina/NeteaseCloudMusicApiReborn/blob/main/LICENSE)
|
||||
console.log(error)
|
||||
|
@ -1,6 +1,7 @@
|
||||
const fsPromises = require('fs/promises')
|
||||
const path = require('path')
|
||||
const server = require('../server')
|
||||
const logger = require('../util/logger.js')
|
||||
|
||||
const exportFile = path.join(__dirname, 'moddef.json')
|
||||
|
||||
@ -16,7 +17,7 @@ async function main() {
|
||||
)
|
||||
|
||||
fsPromises.writeFile(exportFile, JSON.stringify(def, null, 4))
|
||||
console.log(`👍 Get your own definition at: ${exportFile}`)
|
||||
logger.info(`👍 Get your own definition at: ${exportFile}`)
|
||||
}
|
||||
|
||||
main()
|
||||
|
@ -4,6 +4,7 @@
|
||||
// 可按需修改此 API 的代码
|
||||
/* {"extInfo":"{\"lastRequestTimestamp\":1692358373509,\"lbsInfoList\":[{\"lat\":40.23076381,\"lon\":129.07545186,\"time\":1692358543},{\"lat\":40.23076381,\"lon\":129.07545186,\"time\":1692055283}],\"listenedTs\":false,\"noAidjToAidj\":true}","header":"{}"} */
|
||||
|
||||
const logger = require('../util/logger.js')
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = (query, request) => {
|
||||
var extInfo = {}
|
||||
@ -22,6 +23,6 @@ module.exports = (query, request) => {
|
||||
const data = {
|
||||
extInfo: JSON.stringify(extInfo),
|
||||
}
|
||||
// console.log(data)
|
||||
// logger.info(data)
|
||||
return request(`/api/aidj/content/rcmd/info`, data, createOption(query))
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ const mm = require('music-metadata')
|
||||
const uploadPlugin = require('../plugins/songUpload')
|
||||
const md5 = require('md5')
|
||||
const createOption = require('../util/option.js')
|
||||
const logger = require('../util/logger.js')
|
||||
module.exports = async (query, request) => {
|
||||
let ext = 'mp3'
|
||||
// if (query.songFile.name.indexOf('flac') > -1) {
|
||||
@ -65,7 +66,7 @@ module.exports = async (query, request) => {
|
||||
}
|
||||
// if (metadata.native.ID3v1) {
|
||||
// metadata.native.ID3v1.forEach((item) => {
|
||||
// // console.log(item.id, item.value)
|
||||
// // logger.info(item.id, item.value)
|
||||
// if (item.id === 'title') {
|
||||
// songName = item.value
|
||||
// }
|
||||
@ -76,19 +77,19 @@ module.exports = async (query, request) => {
|
||||
// album = item.value
|
||||
// }
|
||||
// })
|
||||
// // console.log({
|
||||
// // logger.info({
|
||||
// // songName,
|
||||
// // album,
|
||||
// // songName,
|
||||
// // })
|
||||
// }
|
||||
// console.log({
|
||||
// logger.info({
|
||||
// songName,
|
||||
// album,
|
||||
// songName,
|
||||
// })
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
logger.info(error)
|
||||
}
|
||||
const tokenRes = await request(
|
||||
`/api/nos/token/alloc`,
|
||||
@ -106,9 +107,9 @@ module.exports = async (query, request) => {
|
||||
|
||||
if (res.body.needUpload) {
|
||||
const uploadInfo = await uploadPlugin(query, request)
|
||||
// console.log('uploadInfo', uploadInfo.body.result.resourceId)
|
||||
// logger.info('uploadInfo', uploadInfo.body.result.resourceId)
|
||||
}
|
||||
// console.log(tokenRes.body.result)
|
||||
// logger.info(tokenRes.body.result)
|
||||
const res2 = await request(
|
||||
`/api/upload/cloud/info/v2`,
|
||||
{
|
||||
@ -123,8 +124,8 @@ module.exports = async (query, request) => {
|
||||
},
|
||||
createOption(query),
|
||||
)
|
||||
// console.log({ res2, privateCloud: res2.body.privateCloud })
|
||||
// console.log(res.body.songId, 'songid')
|
||||
// logger.info({ res2, privateCloud: res2.body.privateCloud })
|
||||
// logger.info(res.body.songId, 'songid')
|
||||
const res3 = await request(
|
||||
`/api/cloud/pub/v2`,
|
||||
{
|
||||
@ -132,7 +133,7 @@ module.exports = async (query, request) => {
|
||||
},
|
||||
createOption(query),
|
||||
)
|
||||
// console.log({ res3 })
|
||||
// logger.info({ res3 })
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
|
@ -1,4 +1,5 @@
|
||||
const createOption = require('../util/option.js')
|
||||
const logger = require('../util/logger.js')
|
||||
module.exports = async (query, request) => {
|
||||
query.ids = query.ids || ''
|
||||
const data = {
|
||||
@ -9,7 +10,7 @@ module.exports = async (query, request) => {
|
||||
}),
|
||||
),
|
||||
}
|
||||
console.log(data)
|
||||
logger.info(data)
|
||||
|
||||
return request(`/api/playlist/track/add`, data, createOption(query, 'weapi'))
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
// 支持qq音乐、酷狗音乐、酷我音乐、咪咕音乐、第三方网易云API等等(来自GD音乐台)
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
|
||||
const logger = require('../util/logger.js')
|
||||
|
||||
module.exports = async (query, request) => {
|
||||
try {
|
||||
@ -12,7 +12,7 @@ module.exports = async (query, request) => {
|
||||
const server = query.server ? query.server.split(',') : query.server
|
||||
const result = await match(query.id, !server? source : server)
|
||||
const proxy = process.env.PROXY_URL;
|
||||
console.log("[OK] 开始解灰", query.id, result)
|
||||
logger.info("开始解灰", query.id, result)
|
||||
const useProxy = process.env.ENABLE_PROXY || "false"
|
||||
if (result.url.includes('kuwo')) { result.proxyUrl = useProxy === 'true' ? proxy + result.url : result.url }
|
||||
return {
|
||||
|
@ -2,7 +2,7 @@
|
||||
// 支持qq音乐、酷狗音乐、酷我音乐、咪咕音乐、第三方网易云API等等(来自GD音乐台)
|
||||
|
||||
const createOption = require('../util/option.js')
|
||||
|
||||
const logger = require('../util/logger.js')
|
||||
|
||||
module.exports = async (query, request) => {
|
||||
try {
|
||||
@ -12,7 +12,7 @@ module.exports = async (query, request) => {
|
||||
const server = query.server ? query.server.split(',') : query.server
|
||||
const result = await match(query.id, !server? source : server)
|
||||
const proxy = process.env.PROXY_URL;
|
||||
console.log("[OK] 开始解灰", query.id, result)
|
||||
logger.info("开始解灰", query.id, result)
|
||||
const useProxy = process.env.ENABLE_PROXY || "false"
|
||||
if (result.url.includes('kuwo') && useProxy === "true") { result.proxyUrl = proxy + result.url }
|
||||
return {
|
||||
|
@ -3,6 +3,7 @@
|
||||
// 而是采用 standard, exhigh, lossless, hires, jyeffect(高清环绕声), sky(沉浸环绕声), jymaster(超清母带) 进行音质判断
|
||||
// 当unblock为true时, 会尝试使用unblockneteasemusic进行解锁, 同时音质设置不会生效, 但仍然为必须传入参数
|
||||
|
||||
const logger = require('../util/logger.js')
|
||||
const createOption = require('../util/option.js')
|
||||
module.exports = async (query, request) => {
|
||||
const match = require('@unblockneteasemusic/server')
|
||||
@ -16,7 +17,7 @@ module.exports = async (query, request) => {
|
||||
if (query.unblock === 'true') {
|
||||
try {
|
||||
const result = await match(query.id, source)
|
||||
console.log('[OK] 开始解灰', query.id, result)
|
||||
logger.info('开始解灰', query.id, result)
|
||||
if (result.url.includes('kuwo')) {
|
||||
const useProxy = process.env.ENABLE_PROXY || 'false'
|
||||
var proxyUrl = useProxy === 'true' ? process.env.PROXY_URL + result.url : result.url
|
||||
|
@ -1,4 +1,5 @@
|
||||
const { eapiResDecrypt, eapiReqDecrypt } = require('../util/crypto')
|
||||
const logger = require('../util/logger.js')
|
||||
|
||||
let reqParamsHex =
|
||||
'6E0B1C712DCB3648589D7C950C296204072A88C3E080C4CFFD0A71A553EC2533BA88E11B1E1C6DF3BE8EFA26177FCB6FCA34EB3FAFAB4671B2BBAFA9781AFDA2BF53A3DC423722493837B9BC6E80CED5BBD2DDC2856920E4D4E3E7F3EB77ECF265217A66AE677BE36F2D6FB203F721CA250E1453EA61A34904E33D5FCB9D483601D744BE0AE979AC911A00F25828538844F4B1C24F6C34880A4AB257F530C7FB10A81FED32B18D09D70C0B1B9D34A2E58A3C3FAD382C6F958077059C4F801AD7B3B248FDB9D7A59B6A9EEFF8C781A84315B33A7AFD48BE9FCFCBE1902CCC27949ACF2BDE3FA34D116E230C3597E8320B8C42BBBF371A00C03EC428E0440EB94C1540F3FD4173D29E310AFE43AB0EF449852904103EF305FC435AD43B7D8673642F74C89CCB2F1A6A79B3BE14F1235D3843C3B241D12C05DBDDF37B68CA8B5D0230AF1FCF2A9705886F4D126B33FFF6948DE1E4046DB6423D687E96C5B65122464D2E71AEC7722935FF2C3796FAE253A16AA3B102FBE7296AB0DB9EA5C46AD12B'.replaceAll(
|
||||
@ -10,6 +11,6 @@ const resHex =
|
||||
' ',
|
||||
'',
|
||||
)
|
||||
console.log(reqParamsHex)
|
||||
console.log(eapiReqDecrypt(reqParamsHex))
|
||||
console.log(eapiResDecrypt(resHex))
|
||||
logger.info(reqParamsHex)
|
||||
logger.info(eapiReqDecrypt(reqParamsHex))
|
||||
logger.info(eapiResDecrypt(resHex))
|
||||
|
@ -2,6 +2,7 @@ const { cloud, login_cellphone } = require('../main')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const yargs = require('yargs')
|
||||
const logger = require('../util/logger.js')
|
||||
|
||||
const MUSIC_FILE_EXTENSIONS = new Set(['.mp3', '.flac'])
|
||||
|
||||
@ -37,11 +38,11 @@ async function uploadArrayOfFile(token, arrayOfFiles) {
|
||||
cookie: token.body.cookie,
|
||||
})
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
logger.info(error)
|
||||
failed += 1
|
||||
failedFiles.push(file)
|
||||
}
|
||||
console.log(`Uploaded ${k + 1}/${fileCount} songs`)
|
||||
logger.info(`Uploaded ${k + 1}/${fileCount} songs`)
|
||||
}
|
||||
return { failedFiles, failed }
|
||||
}
|
||||
@ -86,19 +87,19 @@ async function main() {
|
||||
const files = args.file ? [args.file] : getAllMusicFiles(args.dir)
|
||||
const fileCount = files.length
|
||||
|
||||
console.log(`Found ${fileCount} files, uploading...`)
|
||||
logger.info(`Found ${fileCount} files, uploading...`)
|
||||
let res = await uploadArrayOfFile(token, files)
|
||||
if (res.failed) {
|
||||
console.log(`Failed to upload ${res.failed} songs, retrying...`)
|
||||
logger.info(`Failed to upload ${res.failed} songs, retrying...`)
|
||||
res = await uploadArrayOfFile(token, res.failedFiles)
|
||||
}
|
||||
|
||||
console.log(`Uploaded ${fileCount - res.failed} songs`)
|
||||
console.log(
|
||||
logger.info(`Uploaded ${fileCount - res.failed} songs`)
|
||||
logger.info(
|
||||
`Failed to upload ${res.failed} songs, you can reupload the files below`,
|
||||
)
|
||||
for (let k in res.failedFiles) {
|
||||
console.log(res.failedFiles[k])
|
||||
logger.info(res.failedFiles[k])
|
||||
}
|
||||
}
|
||||
main()
|
||||
|
@ -1,6 +1,7 @@
|
||||
const { cloud, login_cellphone } = require('../main')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const logger = require('../util/logger.js')
|
||||
|
||||
async function main() {
|
||||
const result = await login_cellphone({
|
||||
@ -17,7 +18,7 @@ async function main() {
|
||||
cookie: result.body.cookie,
|
||||
})
|
||||
} catch (error) {
|
||||
console.log(error, 'error')
|
||||
logger.info(error, 'error')
|
||||
}
|
||||
}
|
||||
main()
|
||||
|
@ -1,3 +1,4 @@
|
||||
const logger = require('../util/logger.js')
|
||||
const {
|
||||
login_cellphone,
|
||||
user_cloud,
|
||||
@ -10,22 +11,22 @@ async function test() {
|
||||
phone: '手机号',
|
||||
password: '密码',
|
||||
})
|
||||
console.log(result)
|
||||
logger.info(result)
|
||||
const result2 = await user_cloud({
|
||||
cookie: result.body.cookie,
|
||||
})
|
||||
console.log(result2.body)
|
||||
logger.info(result2.body)
|
||||
const result3 = await album_sublist({
|
||||
cookie: result.body.cookie,
|
||||
})
|
||||
console.log(result3.body)
|
||||
logger.info(result3.body)
|
||||
const result4 = await song_url({
|
||||
cookie: result.body.cookie,
|
||||
id: 33894312,
|
||||
})
|
||||
console.log(result4.body)
|
||||
logger.info(result4.body)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
logger.info(error)
|
||||
}
|
||||
}
|
||||
test()
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { banner, lyric } from '@neteaseapireborn/api'
|
||||
import { logger } from '../util/logger.js'
|
||||
banner({ type: 0 }).then((res) => {
|
||||
console.log(res)
|
||||
logger.info(res)
|
||||
})
|
||||
lyric({
|
||||
id: '33894312',
|
||||
}).then((res) => {
|
||||
console.log(res)
|
||||
logger.info(res)
|
||||
})
|
||||
|
@ -1,5 +1,6 @@
|
||||
const { default: axios } = require('axios')
|
||||
const createOption = require('../util/option.js')
|
||||
const logger = require('../util/logger.js')
|
||||
module.exports = async (query, request) => {
|
||||
let ext = 'mp3'
|
||||
// if (query.songFile.name.indexOf('flac') > -1) {
|
||||
@ -51,7 +52,7 @@ module.exports = async (query, request) => {
|
||||
maxBodyLength: Infinity,
|
||||
})
|
||||
} catch (error) {
|
||||
console.log('error', error.response)
|
||||
logger.info('error', error.response)
|
||||
throw error.response
|
||||
}
|
||||
return {
|
||||
|
@ -1,5 +1,6 @@
|
||||
'use strict'
|
||||
const WASM_BINARY_PLACEHOLDER = 'WASM_BINARY_PLACEHOLDER';
|
||||
const logger = require('../../util/logger.js')
|
||||
// See https://github.com/Distributive-Network/PythonMonkey/issues/266
|
||||
if (typeof globalThis.setInterval != 'function'){
|
||||
globalThis.setInterval = function pm$$setInterval(fn, timeout) {
|
||||
@ -1611,9 +1612,9 @@ function instantiateRuntime(){
|
||||
|
||||
function GenerateFP(floatArray) {
|
||||
let PCMBuffer = Float32Array.from(floatArray)
|
||||
console.log('[afp] input samples n=', PCMBuffer.length)
|
||||
logger.info('[afp] input samples n=', PCMBuffer.length)
|
||||
return instantiateRuntime().then((fpRuntime) => {
|
||||
console.log('[afp] begin fingerprinting')
|
||||
logger.info('[afp] begin fingerprinting')
|
||||
let fp_vector = fpRuntime.ExtractQueryFP(PCMBuffer.buffer)
|
||||
let result_buf = new Uint8Array(fp_vector.size());
|
||||
for (let t = 0; t < fp_vector.size(); t++)
|
||||
|
@ -1,6 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
@ -9,26 +8,23 @@
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<a href="/qrlogin-nocookie.html">
|
||||
如果没登录,请先登录
|
||||
</a>
|
||||
<a href="/qrlogin-nocookie.html"> 如果没登录,请先登录 </a>
|
||||
</div>
|
||||
<input id="file" type="file" multiple />
|
||||
<div id="app">
|
||||
<ul>
|
||||
<li v-for="(item,index) in songs" :key="index">
|
||||
{{item.songName}}
|
||||
</li>
|
||||
<li v-for="(item,index) in songs" :key="index">{{item.songName}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
||||
<script src="https://fastly.jsdelivr.net/npm/vue"></script>
|
||||
<script>
|
||||
const logger = require('../util/logger.js')
|
||||
const app = Vue.createApp({
|
||||
data() {
|
||||
return {
|
||||
songs: []
|
||||
songs: [],
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@ -36,16 +32,18 @@
|
||||
},
|
||||
methods: {
|
||||
getData() {
|
||||
console.log('getdata');
|
||||
logger.info('getdata')
|
||||
const _this = this
|
||||
axios({
|
||||
url: `/user/cloud?time=${Date.now()}&cookie=${localStorage.getItem('cookie')}`,
|
||||
}).then(res => {
|
||||
console.log(res.data)
|
||||
url: `/user/cloud?time=${Date.now()}&cookie=${localStorage.getItem(
|
||||
'cookie',
|
||||
)}`,
|
||||
}).then((res) => {
|
||||
logger.info(res.data)
|
||||
_this.songs = res.data.data
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}).mount('#app')
|
||||
const fileUpdateTime = {}
|
||||
let fileLength = 0
|
||||
@ -54,7 +52,7 @@
|
||||
document
|
||||
.querySelector('input[type="file"]')
|
||||
.addEventListener('change', function (e) {
|
||||
console.log(this.files)
|
||||
logger.info(this.files)
|
||||
let currentIndx = 0
|
||||
fileLength = this.files.length
|
||||
for (const item of this.files) {
|
||||
@ -70,19 +68,27 @@
|
||||
formData.append('songFile', file)
|
||||
axios({
|
||||
method: 'post',
|
||||
url: `/cloud?time=${Date.now()}&cookie=${localStorage.getItem('cookie')}`,
|
||||
url: `/cloud?time=${Date.now()}&cookie=${localStorage.getItem(
|
||||
'cookie',
|
||||
)}`,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
data: formData,
|
||||
}).then(res => {
|
||||
console.log(`${file.name} 上传成功`)
|
||||
if (currentIndx >= fileLength) { console.log('上传完毕') }
|
||||
})
|
||||
.then((res) => {
|
||||
logger.info(`${file.name} 上传成功`)
|
||||
if (currentIndx >= fileLength) {
|
||||
logger.info('上传完毕')
|
||||
}
|
||||
app.getData()
|
||||
}).catch(async err => {
|
||||
console.log(err)
|
||||
console.log(fileUpdateTime)
|
||||
fileUpdateTime[file.name] ? fileUpdateTime[file.name] += 1 : fileUpdateTime[file.name] = 1
|
||||
})
|
||||
.catch(async (err) => {
|
||||
logger.info(err)
|
||||
logger.info(fileUpdateTime)
|
||||
fileUpdateTime[file.name]
|
||||
? (fileUpdateTime[file.name] += 1)
|
||||
: (fileUpdateTime[file.name] = 1)
|
||||
if (fileUpdateTime[file.name] >= 4) {
|
||||
console.error(`丢,这首歌怎么都传不上:${file.name}`)
|
||||
return
|
||||
@ -95,5 +101,4 @@
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
@ -53,16 +53,20 @@ $ set HOST=127.0.0.1 && node app.js
|
||||
```
|
||||
|
||||
### npx 方式运行
|
||||
|
||||
支持 npx 方式运行,会自动安装依赖和运行
|
||||
|
||||
```
|
||||
npx @neteaseapireborn/api@版本号
|
||||
```
|
||||
|
||||
或者运行
|
||||
|
||||
```
|
||||
npx @neteaseapireborn/api@latest
|
||||
|
||||
```
|
||||
|
||||
此命令每次执行都会使用最新版
|
||||
|
||||
## Vercel 部署
|
||||
@ -80,22 +84,27 @@ v4.0.8 加入了 Vercel 配置文件,可以直接在 Vercel 下部署了,不需
|
||||
5. 直接点`Continue`
|
||||
6. `PROJECT NAME`自己填,`FRAMEWORK PRESET` 选 `Other` 然后直接点 `Deploy` 接着等部署完成即可
|
||||
|
||||
|
||||
## 腾讯云 serverless 部署
|
||||
|
||||
因 `Vercel` 在国内访问太慢(不绑定自己的域名的情况下),在此提供腾讯云 serverless 部署方法
|
||||
|
||||
### 操作方法
|
||||
|
||||
1. [fork](https://github.com/neteasecloudmusicapienhanced/api-enhanced/fork) 此项目
|
||||
2. 在腾讯云 serverless 应用管理页面( https://console.cloud.tencent.com/sls ),点击`新建应用`
|
||||
3. 顶部`创建方式`选择 `Web 应用`
|
||||
4. 选择 `Express框架`,点击底部`下一步按钮`
|
||||
5. 输入`应用名`,上传方式选择`代码仓库`,进行 GitHub 授权(如已授权可跳过这一步),代码仓库选择刚刚 fork 的项目
|
||||
6. 启动文件填入:
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
export PORT=9000
|
||||
/var/lang/node16/bin/node app.js
|
||||
```
|
||||
|
||||
7. 点击`完成`,等待部署完成,点击`资源列表`的 `API网关` 里的 `URL`,正常情况会打开文档地址,点击文档`例子`可查看接口调用效果
|
||||
|
||||
- 注意
|
||||
- 腾讯云 serverless 并不是免费的,前三个月有免费额度,之后收费
|
||||
- 当前(2024-08-24), 用此法创建的话, 会`默认`关联一个"日志服务-日志主题"(创建过程中没有提醒), 此服务是计量收费的
|
||||
@ -151,8 +160,9 @@ banner({ type: 0 }).then((res) => {
|
||||
})
|
||||
```
|
||||
|
||||
## Docker 容器运行 (原版 API, 不推荐)
|
||||
|
||||
## Docker 容器运行
|
||||
> 注意: 该部分为**原版网易云音乐 API**的 Docker 容器部署方式, 关于增强版本请**自行构建并部署**
|
||||
|
||||
> 注意: 在 docker 中运行的时候, 由于使用了 request 来发请求, 所以会检查几个 proxy 相关的环境变量(如下所列), 这些环境变量 会影响到 request 的代理, 详情请参考[request 的文档](https://github.com/request/request#proxies), 如果这些环境变量 指向的代理不可用, 那么就会造成错误, 所以在使用 docker 的时候一定要注意这些环境变量. 不过, 要是你在 query 中加上了 proxy 参数, 那么环境变量会被覆盖, 就会用你通过 proxy 参数提供的代理了.
|
||||
|
||||
@ -182,7 +192,7 @@ docker run -d -p 3000:3000 --name netease_cloud_music_api -e http_proxy= -e http
|
||||
docker run -d -p 3000:3000 -e http_proxy= -e https_proxy= -e no_proxy= -e HTTP_PROXY= -e HTTPS_PROXY= -e NO_PROXY= binaryify/netease_cloud_music_api
|
||||
```
|
||||
|
||||
> 以下是自行 build docker 镜像方式
|
||||
### 自行构建 docker 镜像 (推荐)
|
||||
|
||||
```
|
||||
$ git clone https://github.com/neteasecloudmusicapienhanced/api-enhanced.git && cd api
|
||||
@ -196,7 +206,7 @@ $ sudo docker run -d -p 3000:3000 netease-music-api
|
||||
|
||||
### 调用前须知
|
||||
|
||||
!> 本项目不提供线上 demo,请不要轻易信任使用他人提供的公开服务,以免发生安全问题,泄露自己的账号和密码
|
||||
!> 本项目不提供线上 demo, 只提供在线文档服务, 请不要轻易信任使用他人提供的公开服务,以免发生安全问题,泄露自己的账号和密码
|
||||
|
||||
!> 为使用方便,降低门槛, 文档示例接口直接使用了 GET 请求,本项目同时支持 GET/POST 请按实际需求使用 (POST 请求 url 必须添加时间戳,使每次请求 url 不一样,不然请求会被缓存)
|
||||
|
||||
@ -234,7 +244,11 @@ $ sudo docker run -d -p 3000:3000 netease-music-api
|
||||
|
||||
不要频繁调登录接口,不然可能会被风控,登录状态还存在就不要重复调登录接口
|
||||
|
||||
~~因网易增加了网易云盾验证,密码登录暂时不要使用,尽量使用短信验证码登录和二维码登录,否则调用某些接口会触发需要验证的错误~~
|
||||
!> ~~因网易增加了网易云盾验证,密码登录暂时不要使用,尽量使用短信验证码登录和二维码登录,否则调用某些接口会触发需要验证的错误~~
|
||||
|
||||
!> ~~二开作者注: 现在网易云云盾验证再次升级, 导致现在短信验证码也没法用了~~
|
||||
|
||||
!> 二开作者再注: 现在二维码登录也无法使用了, 网易云官方最近查的太严了, 现在尝试调用会提示环境异常, 如果各位有绕过的方法请一定开`Pull Request`
|
||||
|
||||
#### 1. 手机登录
|
||||
|
||||
@ -281,9 +295,11 @@ v3.30.0 后支持手动传入 cookie,登录接口返回内容新增 `cookie` 字
|
||||
cookie:"xxx"
|
||||
}
|
||||
```
|
||||
|
||||
另外的 cookie 说明:
|
||||
可以直接从浏览器中获取 cookie 值, 只需要其中 key 为`MUSIC_U`的数据即可
|
||||
请求
|
||||
|
||||
```
|
||||
GET https://example.com/search?keywords=HELLO&cookie=MUSIC_U%3Dxxxx
|
||||
POST https://example.com/search?keywords=HELLO
|
||||
@ -331,13 +347,12 @@ body {
|
||||
|
||||
`/public/qrlogin-nocookie.html` (访问地址:http://localhost:3000/qrlogin-nocookie.html)
|
||||
|
||||
|
||||
#### 3. 游客登录
|
||||
|
||||
说明 : 直接调用此接口, 可获取游客 cookie,如果遇到其他接口未登录状态报 400 状态码需要验证的错误,可使用此接口获取游客 cookie 避免报错
|
||||
|
||||
**接口地址 :** `/register/anonimous`
|
||||
|
||||
|
||||
#### 注意
|
||||
|
||||
调用登录接口的速度比调用其他接口慢 , 因为登录过程调用了加密算法
|
||||
@ -432,7 +447,6 @@ body {
|
||||
|
||||
**调用例子 :** `/nickname/check?nickname=binaryify`
|
||||
|
||||
|
||||
### 更换绑定手机
|
||||
|
||||
说明 : 调用此接口 ,可更换绑定手机(流程:先发送验证码到原手机号码,再发送验证码到新手机号码然后再调用此接口)
|
||||
@ -595,7 +609,6 @@ signature:用户签名
|
||||
|
||||
**调用例子 :** `/user/playlist?uid=32953014`
|
||||
|
||||
|
||||
### 更新歌单
|
||||
|
||||
说明 : 登录后调用此接口,可以更新用户歌单
|
||||
@ -1181,7 +1194,6 @@ tags: 歌单标签
|
||||
|
||||
> 如果你设置 limit=50&offset=100,你就会得到第 101-150 首歌曲
|
||||
|
||||
|
||||
### 歌单详情动态
|
||||
|
||||
说明 : 调用后可获取歌单详情动态部分,如评论数,是否收藏,播放数
|
||||
@ -1192,7 +1204,6 @@ tags: 歌单标签
|
||||
|
||||
**调用例子 :** `/playlist/detail/dynamic?id=24381616`
|
||||
|
||||
|
||||
### 歌单更新播放量
|
||||
|
||||
说明 : 调用后可更新歌单播放量
|
||||
@ -1203,9 +1214,6 @@ tags: 歌单标签
|
||||
|
||||
**调用例子 :** `/playlist/update/playcount?id=24381616`
|
||||
|
||||
|
||||
|
||||
|
||||
### 获取音乐 url
|
||||
|
||||
说明 : 使用歌单详情接口后 , 能得到的音乐的 id, 但不能得到的音乐 url, 调用此接口, 传入的音乐 id( 可多个 , 用逗号隔开 ), 可以获取对应的音乐的 url,未登录状态或者非会员返回试听片段(返回字段包含被截取的正常歌曲的开始时间和结束时间)
|
||||
@ -1350,6 +1358,8 @@ tags: 歌单标签
|
||||
|
||||
说明 : 调用此接口 , 传入类型和歌单 id 可收藏歌单或者取消收藏歌单
|
||||
|
||||
!> 警告: 在`v4.25.0`版本后, 在网易云登陆后请求不要带上`cookie`字段, 会导致请求不合法
|
||||
|
||||
**必选参数 :**
|
||||
|
||||
`t` : 类型,1:收藏,2:取消收藏
|
||||
@ -1379,6 +1389,8 @@ tags: 歌单标签
|
||||
|
||||
说明 : 调用此接口 , 可以添加歌曲到歌单或者从歌单删除某首歌曲 ( 需要登录 )
|
||||
|
||||
!> 警告: 在`v4.25.0`版本后, 在网易云登陆后请求不要带上`cookie`字段, 会导致请求不合法
|
||||
|
||||
**必选参数 :**
|
||||
|
||||
`op`: 从歌单增加单曲为 add, 删除为 del
|
||||
@ -1435,42 +1447,47 @@ tags: 歌单标签
|
||||
|
||||
**调用例子 :** `/lyric?id=33894312`
|
||||
|
||||
|
||||
### 获取逐字歌词
|
||||
|
||||
说明 : 此接口的 `yrc` 字段即为逐字歌词 (可能有歌曲不包含逐字歌词)
|
||||
|
||||
|
||||
**必选参数 :** `id`: 音乐 id
|
||||
|
||||
**接口地址 :** `/lyric/new`
|
||||
|
||||
**调用例子 :** `/lyric/new?id=1824020871`
|
||||
|
||||
|
||||
相关讨论可见: [Issue](https://github.com/Binaryify/NeteaseCloudMusicApi/issues/1667)
|
||||
|
||||
**歌词格式解析 :**
|
||||
|
||||
当逐字歌词适用时,`yrc`的`lyric`字段包括形式如下的内容
|
||||
* (可能存在)JSON 歌曲元数据
|
||||
|
||||
- (可能存在)JSON 歌曲元数据
|
||||
|
||||
```
|
||||
{"t":0,"c":[{"tx":"作曲: "},{"tx":"柳重言","li":"http://p1.music.126.net/Icj0IcaOjH2ZZpyAM-QGoQ==/6665239487822533.jpg","or":"orpheus://nm/artist/home?id=228547&type=artist"}]}
|
||||
{"t":5403,"c":[{"tx":"编曲: "},{"tx":"Alex San","li":"http://p1.music.126.net/pSbvYkrzZ1RFKqoh-fA9AQ==/109951166352922615.jpg","or":"orpheus://nm/artist/home?id=28984845&type=artist"}]}
|
||||
{"t":10806,"c":[{"tx":"制作人: "},{"tx":"王菲","li":"http://p1.music.126.net/1KQVD6XWbs5IAV0xOj1ZIA==/18764265441342019.jpg","or":"orpheus://nm/artist/home?id=9621&type=artist"},{"tx":"/"},{"tx":"梁荣骏","li":"http://p1.music.126.net/QrD8drwrRcegfKLPoiiG2Q==/109951166288436155.jpg","or":"orpheus://nm/artist/home?id=189294&type=artist"}]}
|
||||
```
|
||||
|
||||
该字段不一定出现;可能出现的数据意义有:
|
||||
|
||||
- `t` : 数据显示开始时间戳 (毫秒)
|
||||
- `c` : 元数据 list
|
||||
- `tx`: 文字段
|
||||
- `li`: 艺术家、歌手头像图 url
|
||||
- `or`:云音乐 app 内路径;例中作用即打开艺术家主页
|
||||
|
||||
* 逐字歌词
|
||||
|
||||
```
|
||||
[16210,3460](16210,670,0)还(16880,410,0)没...
|
||||
~~~~1 ~~~2 ~~~~3 ~~4 5 ~6 (...)
|
||||
```
|
||||
|
||||
由标号解释:
|
||||
|
||||
1. 歌词行显示开始时间戳 (毫秒)
|
||||
2. 歌词行显示总时长(毫秒)
|
||||
3. 逐字显示开始时间戳 (毫秒)
|
||||
@ -1536,7 +1553,6 @@ tags: 歌单标签
|
||||
|
||||
**调用例子 :** `/comment/music?id=186016&limit=1` 对应晴天评论
|
||||
|
||||
|
||||
### 楼层评论
|
||||
|
||||
说明 : 调用此接口 , 传入资源 parentCommentId 和资源类型 type 和资源 id 参数, 可获得该资源的歌曲楼层评论
|
||||
@ -1864,7 +1880,6 @@ tags: 歌单标签
|
||||
|
||||
`type`: 数字,资源类型,对应歌曲,mv,专辑,歌单,电台,视频对应以下类型
|
||||
|
||||
|
||||
```
|
||||
0: 歌曲
|
||||
|
||||
@ -1921,7 +1936,6 @@ tags: 歌单标签
|
||||
|
||||
`type`:资源类型,对应以下类型
|
||||
|
||||
|
||||
```
|
||||
0: 歌曲
|
||||
|
||||
@ -1940,12 +1954,10 @@ tags: 歌单标签
|
||||
7: 电台
|
||||
```
|
||||
|
||||
|
||||
`t`: 操作,1 为点赞,其他为取消点赞
|
||||
|
||||
`id`: 资源 id
|
||||
|
||||
|
||||
**接口地址 :** `/resource/like`
|
||||
|
||||
**调用例子 :** `/resource/like?t=1&type=1&id=5436712`
|
||||
@ -1953,7 +1965,6 @@ tags: 歌单标签
|
||||
注意:如给动态点赞,不需要传入 id,需要传入 `threadId`,可通过 `event`,`/user/event` 接口获取,如:
|
||||
`/resource/like?t=1&type=6&threadId=A_EV_2_6559519868_32953014`
|
||||
|
||||
|
||||
### 获取点赞过的视频
|
||||
|
||||
说明 : 调用此接口, 可获取获取点赞过的视频
|
||||
@ -2122,7 +2133,6 @@ privilege:权限相关信息
|
||||
|
||||
**调用例子 :** `/artists?id=6452`
|
||||
|
||||
|
||||
### 获取歌手 mv
|
||||
|
||||
说明 : 调用此接口 , 传入歌手 id, 可获得歌手 mv 信息 , 具体 mv 播放地址可调
|
||||
@ -2150,7 +2160,6 @@ privilege:权限相关信息
|
||||
|
||||
**调用例子 :** `/artist/album?id=6452&limit=5` ( 周杰伦 )
|
||||
|
||||
|
||||
### 获取歌手描述
|
||||
|
||||
说明 : 调用此接口 , 传入歌手 id, 可获得歌手描述
|
||||
@ -2229,7 +2238,6 @@ privilege:权限相关信息
|
||||
|
||||
**调用例子 :** `/recommend/resource`
|
||||
|
||||
|
||||
### 获取每日推荐歌曲
|
||||
|
||||
说明 : 调用此接口 , 可获得每日推荐歌曲 ( 需要登录 )
|
||||
@ -2238,7 +2246,6 @@ privilege:权限相关信息
|
||||
|
||||
**调用例子 :** `/recommend/songs`
|
||||
|
||||
|
||||
### 每日推荐歌曲-不感兴趣
|
||||
|
||||
说明 : 日推歌曲标记为不感兴趣( 同时会返回一个新推荐歌曲, 需要登录 )
|
||||
@ -2250,6 +2257,7 @@ privilege:权限相关信息
|
||||
**调用例子 :** `/recommend/songs/dislike?id=168091`
|
||||
|
||||
返回数据 :
|
||||
|
||||
```json
|
||||
{
|
||||
"data":{
|
||||
@ -2289,8 +2297,6 @@ privilege:权限相关信息
|
||||
|
||||
**调用例子 :** `/personal_fm`
|
||||
|
||||
|
||||
|
||||
### 签到
|
||||
|
||||
说明 : 调用此接口 , 传入签到类型 ( 可不传 , 默认安卓端签到 ), 可签到 ( 需要登录
|
||||
@ -2302,9 +2308,6 @@ privilege:权限相关信息
|
||||
|
||||
**调用例子 :** `/daily_signin`
|
||||
|
||||
|
||||
|
||||
|
||||
### 乐签信息
|
||||
|
||||
说明 : 调用此接口, 可获取乐签信息
|
||||
@ -2323,11 +2326,8 @@ privilege:权限相关信息
|
||||
|
||||
**调用例子 :** `/like?id=347230`
|
||||
|
||||
|
||||
|
||||
喜欢成功则返回数据的 code 为 200, 其余为失败
|
||||
|
||||
|
||||
### 喜欢音乐列表
|
||||
|
||||
说明 : 调用此接口 , 传入用户 id, 可获取已喜欢音乐 id 列表(id 数组)
|
||||
@ -2348,8 +2348,6 @@ privilege:权限相关信息
|
||||
|
||||
**调用例子 :** `/fm_trash?id=347230`
|
||||
|
||||
|
||||
|
||||
### 新碟上架
|
||||
|
||||
说明 : 调用此接口 , 可获取新碟上架列表 , 如需具体音乐信息需要调用获取专辑列表接
|
||||
@ -2357,8 +2355,6 @@ privilege:权限相关信息
|
||||
|
||||
**可选参数 :**
|
||||
|
||||
|
||||
|
||||
`area`: ALL:全部,ZH:华语,EA:欧美,KR:韩国,JP:日本
|
||||
|
||||
`type` : new:全部 hot:热门,默认为 new
|
||||
@ -2420,8 +2416,6 @@ privilege:权限相关信息
|
||||
|
||||
**调用例子 :** `/top/artists?offset=0&limit=30`
|
||||
|
||||
|
||||
|
||||
### 全部 mv
|
||||
|
||||
说明 : 调用此接口 , 可获取全部 mv
|
||||
@ -2565,8 +2559,6 @@ MV 数据 , 数据包含 mv 名字 , 歌手 , 发布时间 , mv 视频地址等
|
||||
|
||||
**调用例子 :** `/mv/detail?mvid=5436712`
|
||||
|
||||
|
||||
|
||||
### 获取 mv 点赞转发评论数数据
|
||||
|
||||
说明 : 调用此接口 , 传入 mvid ( 在搜索音乐的时候传 type=1004 获得 ) , 可获取对应
|
||||
@ -3545,7 +3537,6 @@ type='1009' 获取其 id, 如`/search?keywords= 代码时间 &type=1009`
|
||||
说明 : 调用此接口 , 传入歌手 id, 可获取歌手粉丝
|
||||
**必选参数 :** `id` : 歌手 id
|
||||
|
||||
|
||||
**接口地址 :** `/artist/fans`
|
||||
|
||||
**调用例子 :** `/artist/fans?id=2116&limit=10&offset=0`
|
||||
@ -3554,7 +3545,6 @@ type='1009' 获取其 id, 如`/search?keywords= 代码时间 &type=1009`
|
||||
|
||||
说明 : 调用此接口 , 传入歌手 id, 可获取歌手粉丝数量
|
||||
|
||||
|
||||
**必选参数 :** `id` : 歌手 id
|
||||
|
||||
**可选参数 :** `limit`: 取出粉丝数量 , 默认为 20
|
||||
@ -3651,7 +3641,6 @@ type='1009' 获取其 id, 如`/search?keywords= 代码时间 &type=1009`
|
||||
|
||||
**调用例子 :** `/vip/info`, `/vip/info?uid=32953014`
|
||||
|
||||
|
||||
### 获取 VIP 信息(app 端)
|
||||
|
||||
说明: 登录后调用此接口,可获取当前 VIP 信息。
|
||||
@ -3662,8 +3651,6 @@ type='1009' 获取其 id, 如`/search?keywords= 代码时间 &type=1009`
|
||||
|
||||
**调用例子 :** `/vip/info/v2`, `/vip/info/v2?uid=32953014`
|
||||
|
||||
|
||||
|
||||
### 音乐人签到
|
||||
|
||||
说明: 音乐人登录后调用此接口,可以完成“登录音乐人中心”任务,然后通过`/musician/cloudbean/obtain`接口可以领取相应的云豆。
|
||||
@ -3986,6 +3973,7 @@ ONLINE 已发布
|
||||
`offset`: 偏移数量 , 用于分页 , 如 :( 评论页数 -1)\*200, 其中 200 为 limit 的值
|
||||
|
||||
### 播客声音搜索
|
||||
|
||||
说明: 可以搜索播客里的声音
|
||||
|
||||
**接口地址:** `/voicelist/list/search`
|
||||
@ -3993,6 +3981,7 @@ ONLINE 已发布
|
||||
**可选参数**
|
||||
|
||||
- 状态(非必填):
|
||||
|
||||
- `displayStatus: null`(默认):返回所有状态的声音
|
||||
- `displayStatus: "ONLINE"`:已发布的声音
|
||||
- `displayStatus: "AUDITING"`:审核中的声音
|
||||
@ -4005,16 +3994,19 @@ ONLINE 已发布
|
||||
- `limit: 20`:每次返回的声音数量(最多 200 个)
|
||||
|
||||
- 搜索关键词:
|
||||
|
||||
- `name: null`:返回所有的声音
|
||||
- `name: [关键词]`:返回包含指定关键词的声音文件
|
||||
|
||||
- `offset: 0`:偏移量,用于分页,默认为 0,表示从第一个声音开始获取
|
||||
|
||||
- 博客:
|
||||
|
||||
- `radioId: null`:返回所有电台的声音
|
||||
- `radioId: [播客id]`:返回特定播客的声音
|
||||
|
||||
- 是否公开:
|
||||
|
||||
- `type: null`:返回所有类型的声音
|
||||
- `type: "PUBLIC"`:返回公开的声音
|
||||
- `type: "PRIVATE"`:返回隐私的声音
|
||||
@ -4077,9 +4069,8 @@ ONLINE 已发布
|
||||
|
||||
`ids`: 播客 id,即 voiceListId,多个以逗号隔开
|
||||
|
||||
|
||||
|
||||
### 播客上传声音
|
||||
|
||||
说明: 可以上传声音到播客,例子在 `/public/voice_upload.html` 访问地址: <a href="/voice_upload.html" target="_blank">/voice_upload.html</a>
|
||||
|
||||
**接口地址:** `/voice/upload`
|
||||
@ -4095,7 +4086,6 @@ ONLINE 已发布
|
||||
|
||||
`description`: 声音介绍
|
||||
|
||||
|
||||
**可选参数:**
|
||||
`songName`: 声音名称
|
||||
|
||||
@ -4112,6 +4102,7 @@ ONLINE 已发布
|
||||
`composedSongs`: 包含歌曲(歌曲 id),多个用逗号隔开
|
||||
|
||||
### 电台排行榜获取
|
||||
|
||||
说明: 调用此接口可以获取电台排行榜
|
||||
|
||||
**接口地址:** `/djRadio/top`
|
||||
@ -4125,8 +4116,8 @@ ONLINE 已发布
|
||||
|
||||
`dataType`: 未知,默认 3
|
||||
|
||||
|
||||
### 获取声音歌词
|
||||
|
||||
说明: 调用此接口可以获取声音歌词
|
||||
|
||||
**接口地址:** `/voice/lyric`
|
||||
@ -4135,6 +4126,7 @@ ONLINE 已发布
|
||||
`id`: 声音 id
|
||||
|
||||
### 验证接口-二维码生成
|
||||
|
||||
说明: 进行某些操作,如关注用户,可能会触发验证,可调用这个接口生成二维码,使用 app 扫码后可解除验证
|
||||
|
||||
**接口地址:** `/verify/getQr`
|
||||
@ -4152,6 +4144,7 @@ ONLINE 已发布
|
||||
`sign`:触发验证后,接口返回的 params 的 sign
|
||||
|
||||
### 验证接口-二维码检测
|
||||
|
||||
说明: 使用此接口,传入`/verify/getQr`接口返回的`qr`字符串,可检测二维码扫描状态
|
||||
|
||||
**接口地址:** `/verify/qrcodestatus`
|
||||
@ -4171,6 +4164,7 @@ qrCodeStatus:10,detailReason:0 二维码已扫描,并且手机号相同
|
||||
qrCodeStatus:20,detailReason:0 验证成功 qrCodeStatus:21,detailReason:0 二维码已失效
|
||||
|
||||
### 听歌识曲
|
||||
|
||||
说明: 使用此接口,上传音频文件或者麦克风采集声音可识别对应歌曲信息,具体调用例子参考 `/audio_match_demo/index.html` (项目文件: `public/audio_match_demo/index.html`)
|
||||
|
||||
**接口地址:** `/audio/match`
|
||||
@ -4182,6 +4176,7 @@ qrCodeStatus:20,detailReason:0 验证成功qrCodeStatus:21,detailReason:0 二
|
||||
`audioFP`: 音频指纹,参考项目调用例子获取
|
||||
|
||||
### 根据 nickname 获取 userid
|
||||
|
||||
说明: 使用此接口,传入用户昵称,可获取对应的用户 id,支持批量获取,多个昵称用`分号(;)`隔开
|
||||
|
||||
**必选参数:**
|
||||
@ -4193,6 +4188,7 @@ qrCodeStatus:20,detailReason:0 验证成功qrCodeStatus:21,detailReason:0 二
|
||||
**调用例子:** `/get/userids?nicknames=binaryify` `/get/userids?nicknames=binaryify;binaryify2`
|
||||
|
||||
### 专辑简要百科信息
|
||||
|
||||
说明: 登录后调用此接口,使用此接口,传入专辑 id,可获取对应的专辑简要百科信息
|
||||
|
||||
**必选参数:**
|
||||
@ -4204,6 +4200,7 @@ qrCodeStatus:20,detailReason:0 验证成功qrCodeStatus:21,detailReason:0 二
|
||||
**调用例子:** `/ugc/album/get?id=168223858`
|
||||
|
||||
### 歌曲简要百科信息
|
||||
|
||||
说明: 登录后调用此接口,使用此接口,传入歌曲 id,可获取对应的歌曲简要百科信息
|
||||
|
||||
**必选参数:**
|
||||
@ -4215,6 +4212,7 @@ qrCodeStatus:20,detailReason:0 验证成功qrCodeStatus:21,detailReason:0 二
|
||||
**调用例子:** `/ugc/song/get?id=2058263032`
|
||||
|
||||
### 歌手简要百科信息
|
||||
|
||||
说明: 登录后调用此接口,使用此接口,传入歌手 id,可获取对应的歌手简要百科信息
|
||||
|
||||
**必选参数:**
|
||||
@ -4226,6 +4224,7 @@ qrCodeStatus:20,detailReason:0 验证成功qrCodeStatus:21,detailReason:0 二
|
||||
**调用例子:** `/ugc/artist/get?id=15396`
|
||||
|
||||
### mv 简要百科信息
|
||||
|
||||
说明: 登录后调用此接口,使用此接口,传入 mv id,可获取对应的 mv 简要百科信息
|
||||
|
||||
**必选参数:**
|
||||
@ -4237,6 +4236,7 @@ qrCodeStatus:20,detailReason:0 验证成功qrCodeStatus:21,detailReason:0 二
|
||||
**调用例子:** `/ugc/mv/get?id=14572641`
|
||||
|
||||
### 搜索歌手
|
||||
|
||||
说明: 登录后调用此接口,使用此接口,传入歌手名关键字或者歌手 id,可获取搜索到的歌手信息
|
||||
|
||||
**必选参数:**
|
||||
@ -4252,6 +4252,7 @@ qrCodeStatus:20,detailReason:0 验证成功qrCodeStatus:21,detailReason:0 二
|
||||
**调用例子:** `/ugc/artist/search?keyword=sasakure`
|
||||
|
||||
### 用户贡献内容
|
||||
|
||||
说明: 登录后调用此接口,使用此接口,可获取当前登录用户贡献内容
|
||||
|
||||
**必选参数:**
|
||||
@ -4276,6 +4277,7 @@ qrCodeStatus:20,detailReason:0 验证成功qrCodeStatus:21,detailReason:0 二
|
||||
**调用例子:** `/ugc/detail`
|
||||
|
||||
### 用户贡献条目、积分、云贝数量
|
||||
|
||||
说明: 登录后调用此接口,使用此接口,可获取当前登录用户贡献条目、积分、云贝数量
|
||||
|
||||
**接口地址:** `/ugc/user/devote`
|
||||
@ -4283,6 +4285,7 @@ qrCodeStatus:20,detailReason:0 验证成功qrCodeStatus:21,detailReason:0 二
|
||||
**调用例子:** `/ugc/user/devote`
|
||||
|
||||
### 年度听歌报告
|
||||
|
||||
说明: 登录后调用此接口,使用此接口,可获取当前登录用户年度听歌报告,目前支持 2017-2024 年的报告
|
||||
|
||||
**必选参数:**
|
||||
@ -4326,6 +4329,7 @@ qrCodeStatus:20,detailReason:0 验证成功qrCodeStatus:21,detailReason:0 二
|
||||
**调用例子:** `/song/music/detail?id=2082700997`
|
||||
|
||||
返回字段说明 :
|
||||
|
||||
```
|
||||
"br": 比特率Bit Rate,
|
||||
"size": 文件大小,
|
||||
@ -4359,7 +4363,6 @@ qrCodeStatus:20,detailReason:0 验证成功qrCodeStatus:21,detailReason:0 二
|
||||
|
||||
**接口地址:** `/personal/fm/mode`
|
||||
|
||||
|
||||
### 获取专辑歌曲的音质
|
||||
|
||||
说明 : 调用后可获取专辑歌曲的音质
|
||||
@ -4370,7 +4373,6 @@ qrCodeStatus:20,detailReason:0 验证成功qrCodeStatus:21,detailReason:0 二
|
||||
|
||||
**调用例子 :** `/album/privilege?id=168223858`
|
||||
|
||||
|
||||
### 歌手详情动态
|
||||
|
||||
说明 : 调用后可获取歌手详情动态部分,如是否关注,视频数
|
||||
@ -4381,7 +4383,6 @@ qrCodeStatus:20,detailReason:0 验证成功qrCodeStatus:21,detailReason:0 二
|
||||
|
||||
**调用例子 :** `/artist/detail/dynamic?id=15396`
|
||||
|
||||
|
||||
### 最近听歌列表
|
||||
|
||||
说明 : 调用后可获取最近听歌列表
|
||||
@ -4425,9 +4426,11 @@ qrCodeStatus:20,detailReason:0 验证成功qrCodeStatus:21,detailReason:0 二
|
||||
为保证成功,请使用 `获取音乐url` 接口获取各文件属性
|
||||
|
||||
其中比特率`bitrate`要进行以下转换
|
||||
|
||||
```
|
||||
bitrate = Math.floor(br / 1000)
|
||||
```
|
||||
|
||||
导入后的文件名后缀均为 `.mp3` 。但用 `获取音乐url` 获取到的文件格式仍然是正确的。
|
||||
|
||||
### 获取客户端歌曲下载链接 - 新版
|
||||
@ -4634,11 +4637,12 @@ bitrate = Math.floor(br / 1000)
|
||||
**可选参数 :**
|
||||
|
||||
`importStarPlaylist` : 是否导入`我喜欢的音乐`, 此项为 true 则不生成新的歌单
|
||||
`playlistName` : 生成的歌单名, 仅文字导入和链接导入支持, 默认为```'导入音乐 '.concat(new Date().toLocaleString())```
|
||||
`playlistName` : 生成的歌单名, 仅文字导入和链接导入支持, 默认为`'导入音乐 '.concat(new Date().toLocaleString())`
|
||||
|
||||
**元数据导入 :**
|
||||
|
||||
`local`: json 类型的字符串, 如:
|
||||
|
||||
```javascript
|
||||
let local = encodeURIComponent(
|
||||
JSON.stringify([
|
||||
@ -4661,6 +4665,7 @@ let local = encodeURIComponent(
|
||||
**文字导入 :**
|
||||
|
||||
`text`: 导入的文字, 如:
|
||||
|
||||
```javascript
|
||||
let text = encodeURIComponent(`アイニーブルー ZLMS
|
||||
ファンタズマ sasakure.UK`)
|
||||
@ -4671,6 +4676,7 @@ let text = encodeURIComponent(`アイニーブルー ZLMS
|
||||
**链接导入 :**
|
||||
|
||||
`link`: 存有歌单链接的数组类型的字符串, 如:
|
||||
|
||||
```javascript
|
||||
let link = encodeURIComponent(
|
||||
JSON.stringify([
|
||||
@ -4679,7 +4685,9 @@ let link = encodeURIComponent(
|
||||
]),
|
||||
)
|
||||
```
|
||||
|
||||
歌单链接来源:
|
||||
|
||||
1. 将歌单分享到微信/微博/QQ 后复制链接
|
||||
2. 直接复制歌单/个人主页链接
|
||||
3. 直接复制文章链接
|
||||
@ -4757,20 +4765,21 @@ let link = encodeURIComponent(
|
||||
`id`: 歌曲 id
|
||||
|
||||
`data`: 存储歌词摘录信息的对象数组的字符串,如:
|
||||
|
||||
```javascript
|
||||
let data = encodeURIComponent(
|
||||
JSON.stringify([
|
||||
{
|
||||
"translateType": 1,
|
||||
"startTimeStamp": 800,
|
||||
"translateLyricsText": "让我逃走吧、声音已经枯萎",
|
||||
"originalLyricsText": "逃がし てくれって声を枯らした"
|
||||
}
|
||||
translateType: 1,
|
||||
startTimeStamp: 800,
|
||||
translateLyricsText: '让我逃走吧、声音已经枯萎',
|
||||
originalLyricsText: '逃がし てくれって声を枯らした',
|
||||
},
|
||||
]),
|
||||
)
|
||||
```
|
||||
|
||||
若需要修改摘录信息, 则需要填入参数```markId```, 修改对应的摘录信息
|
||||
若需要修改摘录信息, 则需要填入参数`markId`, 修改对应的摘录信息
|
||||
|
||||
**接口地址:** `/song/lyrics/mark/add`
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
<script src="https://fastly.jsdelivr.net/npm/vue@3"></script>
|
||||
|
||||
<script>
|
||||
const logger = require('../util/logger.js')
|
||||
const app = Vue.createApp({
|
||||
data() {
|
||||
return {
|
||||
@ -61,7 +62,7 @@
|
||||
}
|
||||
})
|
||||
this.result = JSON.stringify(res.data.data)
|
||||
console.log(res.data);
|
||||
logger.info(res.data);
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
alert(error?.response?.data?.message || '解密失败,数据格式错误')
|
||||
|
@ -1,6 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
@ -9,12 +8,11 @@
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<a href="/qrlogin-nocookie.html">
|
||||
如果没登录,请先登录
|
||||
</a>
|
||||
<a href="/qrlogin-nocookie.html"> 如果没登录,请先登录 </a>
|
||||
</div>
|
||||
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
||||
<script>
|
||||
const logger = require('../util/logger.js')
|
||||
async function main() {
|
||||
const res = await axios({
|
||||
url: `/homepage/block/page`,
|
||||
@ -24,7 +22,7 @@
|
||||
method: 'post',
|
||||
})
|
||||
let cursor = ''
|
||||
console.log(res.data.data)
|
||||
logger.info(res.data.data)
|
||||
if (res.data.data.hasMore) {
|
||||
cursor = res.data.data.cursor
|
||||
const res2 = await axios({
|
||||
@ -40,5 +38,4 @@
|
||||
main()
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
@ -1,27 +1,26 @@
|
||||
<!-- eslint-disable prettier/prettier -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>一起听</title>
|
||||
<script src="https://unpkg.com/petite-vue"></script>
|
||||
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/mdui@1.0.2/dist/css/mdui.min.css" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://unpkg.com/mdui@1.0.2/dist/css/mdui.min.css"
|
||||
/>
|
||||
<script src="https://unpkg.com/mdui@1.0.2/dist/js/mdui.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body class="mdui-container">
|
||||
<div>
|
||||
<a href="/qrlogin.html">
|
||||
如果没登录,请先登录
|
||||
</a>
|
||||
<a href="/qrlogin.html"> 如果没登录,请先登录 </a>
|
||||
</div>
|
||||
<h1>一起听 - 主机模式</h1>
|
||||
<div>消息: {{message}}</div>
|
||||
<audio id="player" autoplay controls>
|
||||
</audio>
|
||||
<audio id="player" autoplay controls></audio>
|
||||
<br />
|
||||
<br />
|
||||
<button v-if="!account.login" @click="login">获取登录状态</button>
|
||||
@ -32,19 +31,25 @@
|
||||
<details>
|
||||
<summary>加入房间</summary>
|
||||
<div><span>房间ID: </span><input v-model="roomInfo.roomId" /></div>
|
||||
<div><span>邀请者 ID: </span><input v-model="roomInfo.inviterId" /></div>
|
||||
<div>
|
||||
<span>邀请者 ID: </span><input v-model="roomInfo.inviterId" />
|
||||
</div>
|
||||
<button @click="joinRoom">点击加入</button>
|
||||
</details>
|
||||
|
||||
<div v-if="roomInfo.roomId">
|
||||
<div>分享链接为:
|
||||
<div>
|
||||
分享链接为:
|
||||
https://st.music.163.com/listen-together/share/?songId=1372188635&roomId={{roomInfo.roomId}}&inviterId={{roomInfo.inviterId}}
|
||||
</div>
|
||||
<br />
|
||||
<button @click="refreshRoom">刷新房间状态</button>
|
||||
<div>在线用户:</div>
|
||||
<ul class="mdui-list">
|
||||
<li v-for="user in roomInfo.roomUsers" class="mdui-list-item mdui-ripple">
|
||||
<li
|
||||
v-for="user in roomInfo.roomUsers"
|
||||
class="mdui-list-item mdui-ripple"
|
||||
>
|
||||
<div class="mdui-list-item-avatar">
|
||||
<img :src="user.avatarUrl" />
|
||||
</div>
|
||||
@ -60,14 +65,20 @@
|
||||
<details>
|
||||
<summary>播放列表</summary>
|
||||
<br />
|
||||
<div><span>歌单ID: </span><input v-model="playlistInfo.playlistId" /></div>
|
||||
<div>
|
||||
<span>歌单ID: </span><input v-model="playlistInfo.playlistId" />
|
||||
</div>
|
||||
<button @click="loadPlaylist">加载歌单到播放列表</button>
|
||||
<span>{{playlistInfo.playlistName}}</span>
|
||||
<br />
|
||||
<br />
|
||||
<div>歌单内容:</div>
|
||||
<ul class="mdui-list">
|
||||
<li @click="gotoTrack(track.id)" v-for="track in playlistInfo.playlistTracks" class="mdui-list-item mdui-ripple">
|
||||
<li
|
||||
@click="gotoTrack(track.id)"
|
||||
v-for="track in playlistInfo.playlistTracks"
|
||||
class="mdui-list-item mdui-ripple"
|
||||
>
|
||||
<div class="mdui-list-item-avatar">
|
||||
<img :src="track.al.picUrl" />
|
||||
</div>
|
||||
@ -75,9 +86,9 @@
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
</body>
|
||||
<script>
|
||||
const logger = require('../util/logger.js')
|
||||
PetiteVue.createApp({
|
||||
message: '请点击获取登录状态',
|
||||
account: {
|
||||
@ -129,7 +140,7 @@
|
||||
cookie: localStorage.getItem('cookie'),
|
||||
},
|
||||
})
|
||||
console.log(res)
|
||||
logger.info(res)
|
||||
if (res.data.code != 200) {
|
||||
this.message = '加入房间出现问题: ' + res.data.message
|
||||
} else {
|
||||
@ -142,7 +153,7 @@
|
||||
cookie: localStorage.getItem('cookie'),
|
||||
},
|
||||
})
|
||||
console.log(res2)
|
||||
logger.info(res2)
|
||||
const res3 = await axios({
|
||||
url: 'listentogether/sync/playlist/get',
|
||||
method: 'post',
|
||||
@ -152,9 +163,9 @@
|
||||
},
|
||||
})
|
||||
|
||||
this.playlistInfo.playlistName = "其他人的歌单"
|
||||
this.playlistInfo.playlistTrackIds = res3.data.data.playlist.displayList.result
|
||||
.join(',')
|
||||
this.playlistInfo.playlistName = '其他人的歌单'
|
||||
this.playlistInfo.playlistTrackIds =
|
||||
res3.data.data.playlist.displayList.result.join(',')
|
||||
const resa = await axios({
|
||||
url: '/song/detail',
|
||||
method: 'post',
|
||||
@ -163,12 +174,9 @@
|
||||
cookie: localStorage.getItem('cookie'),
|
||||
},
|
||||
})
|
||||
console.log(resa)
|
||||
logger.info(resa)
|
||||
this.playlistInfo.playlistTracks = resa.data.songs
|
||||
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
createRoom: async function () {
|
||||
const res = await axios({
|
||||
@ -178,7 +186,7 @@
|
||||
cookie: localStorage.getItem('cookie'),
|
||||
},
|
||||
})
|
||||
console.log(res)
|
||||
logger.info(res)
|
||||
if (res.data.code != 200) {
|
||||
this.message = '创建房间出现问题: ' + res.data.message
|
||||
} else {
|
||||
@ -193,7 +201,7 @@
|
||||
cookie: localStorage.getItem('cookie'),
|
||||
},
|
||||
})
|
||||
console.log(res2)
|
||||
logger.info(res2)
|
||||
}
|
||||
},
|
||||
refreshRoom: async function () {
|
||||
@ -203,7 +211,7 @@
|
||||
cookie: localStorage.getItem('cookie'),
|
||||
},
|
||||
})
|
||||
console.log(res)
|
||||
logger.info(res)
|
||||
if (res.data.code != 200 || !res.data.data.inRoom) {
|
||||
this.message = '房间状态获取失败, 可能退出了房间'
|
||||
} else {
|
||||
@ -217,10 +225,9 @@
|
||||
data: {
|
||||
roomId: this.roomInfo.roomId,
|
||||
cookie: localStorage.getItem('cookie'),
|
||||
|
||||
},
|
||||
})
|
||||
console.log(res)
|
||||
logger.info(res)
|
||||
if (res.data.code != 200 || !res.data.data.success) {
|
||||
this.message = '房间关闭失败'
|
||||
} else {
|
||||
@ -237,7 +244,7 @@
|
||||
cookie: localStorage.getItem('cookie'),
|
||||
},
|
||||
})
|
||||
console.log(res)
|
||||
logger.info(res)
|
||||
this.playlistInfo.playlistName = res.data.playlist.name
|
||||
this.playlistInfo.playlistTrackIds = res.data.playlist.trackIds
|
||||
.map((track) => track.id)
|
||||
@ -250,7 +257,7 @@
|
||||
cookie: localStorage.getItem('cookie'),
|
||||
},
|
||||
})
|
||||
console.log(resa)
|
||||
logger.info(resa)
|
||||
this.playlistInfo.playlistTracks = resa.data.songs
|
||||
if (this.roomInfo.roomId) {
|
||||
const resb = await axios({
|
||||
@ -267,7 +274,7 @@
|
||||
cookie: localStorage.getItem('cookie'),
|
||||
},
|
||||
})
|
||||
console.log(resb)
|
||||
logger.info(resb)
|
||||
}
|
||||
},
|
||||
gotoTrack: async function (trackId) {
|
||||
@ -310,9 +317,8 @@
|
||||
cookie: localStorage.getItem('cookie'),
|
||||
},
|
||||
})
|
||||
console.log(res)
|
||||
logger.info(res)
|
||||
},
|
||||
}).mount()
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
@ -1,6 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
@ -9,15 +8,16 @@
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<a href="/qrlogin-nocookie.html">
|
||||
如果没登录,请先登录
|
||||
</a>
|
||||
<a href="/qrlogin-nocookie.html"> 如果没登录,请先登录 </a>
|
||||
</div>
|
||||
<div id="app">
|
||||
<ul>
|
||||
<li v-for="(item,index) in voicelist" @click="currentVoiceIndex=index"
|
||||
:class="{active:currentVoiceIndex===index}">
|
||||
<img :src="item.coverUrl" style="width:50px;width:50px;" />
|
||||
<li
|
||||
v-for="(item,index) in voicelist"
|
||||
@click="currentVoiceIndex=index"
|
||||
:class="{active:currentVoiceIndex===index}"
|
||||
>
|
||||
<img :src="item.coverUrl" style="width: 50px; width: 50px" />
|
||||
<ul>
|
||||
<li v-for="(item2,index) in item.voiceListData">
|
||||
{{item2.voiceName}}
|
||||
@ -32,10 +32,10 @@
|
||||
<button @click="submit">上传</button>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="https://fastly.jsdelivr.net/npm/axios@0.26.1/dist/axios.min.js"></script>
|
||||
<script src="https://fastly.jsdelivr.net/npm/vue"></script>
|
||||
<script>
|
||||
const logger = require('../util/logger.js')
|
||||
Vue.createApp({
|
||||
data() {
|
||||
return {
|
||||
@ -62,7 +62,7 @@
|
||||
},
|
||||
methods: {
|
||||
submit() {
|
||||
console.log('submit')
|
||||
logger.info('submit')
|
||||
const file = document.querySelector('input[type=file]').files[0]
|
||||
this.upload(file)
|
||||
},
|
||||
@ -72,14 +72,14 @@
|
||||
url: `/voicelist/search?cookie=${localStorage.getItem('cookie')}`,
|
||||
})
|
||||
|
||||
console.log(res.data.data)
|
||||
logger.info(res.data.data)
|
||||
this.voicelist = res.data.data.list
|
||||
this.voicelist.map(async i => {
|
||||
this.voicelist.map(async (i) => {
|
||||
const res2 = await axios({
|
||||
url: `/voicelist/list?voiceListId=${i.voiceListId}&limit=5`,
|
||||
})
|
||||
i.voiceListData = res2.data.data.list
|
||||
console.log(res2);
|
||||
logger.info(res2)
|
||||
})
|
||||
},
|
||||
upload(file) {
|
||||
@ -87,11 +87,15 @@
|
||||
formData.append('songFile', file)
|
||||
axios({
|
||||
method: 'post',
|
||||
url: `/voice/upload?time=${Date.now()}&cookie=${localStorage.getItem('cookie')
|
||||
}&songName=${this.songName}&voiceListId=${this.currentVoice.voiceListId
|
||||
}&categoryId=${this.currentVoice.categoryId}&coverImgId=${this.currentVoice.coverImgId
|
||||
}&secondCategoryId=${this.currentVoice.secondCategoryId}&description=${this.description
|
||||
}&privacy=1`,
|
||||
url: `/voice/upload?time=${Date.now()}&cookie=${localStorage.getItem(
|
||||
'cookie',
|
||||
)}&songName=${this.songName}&voiceListId=${
|
||||
this.currentVoice.voiceListId
|
||||
}&categoryId=${this.currentVoice.categoryId}&coverImgId=${
|
||||
this.currentVoice.coverImgId
|
||||
}&secondCategoryId=${
|
||||
this.currentVoice.secondCategoryId
|
||||
}&description=${this.description}&privacy=1`,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
@ -100,11 +104,11 @@
|
||||
.then((res) => {
|
||||
alert(`${file.name} 上传成功`)
|
||||
if (currentIndx >= fileLength) {
|
||||
console.log('上传完毕')
|
||||
logger.info('上传完毕')
|
||||
}
|
||||
})
|
||||
.catch(async (err) => {
|
||||
console.log(err)
|
||||
logger.info(err)
|
||||
})
|
||||
},
|
||||
},
|
||||
@ -121,5 +125,4 @@
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
33
server.js
33
server.js
@ -9,6 +9,7 @@ const cache = require('./util/apicache').middleware
|
||||
const { cookieToJson } = require('./util/index')
|
||||
const fileUpload = require('express-fileupload')
|
||||
const decode = require('safe-decode-uri-component')
|
||||
const logger = require('./util/logger.js')
|
||||
|
||||
/**
|
||||
* The version check result.
|
||||
@ -237,14 +238,14 @@ async function consturctServer(moduleDefs) {
|
||||
if (ip == '::1') {
|
||||
ip = global.cnIp
|
||||
}
|
||||
// console.log(ip)
|
||||
// logger.info(ip)
|
||||
obj[3] = {
|
||||
...obj[3],
|
||||
ip,
|
||||
}
|
||||
return request(...obj)
|
||||
})
|
||||
console.log('[OK]', decode(req.originalUrl))
|
||||
logger.info(`Request Success: ${decode(req.originalUrl)}`)
|
||||
|
||||
if (
|
||||
(req.baseUrl === '/song/url/v1' || req.baseUrl === '/song/url') &&
|
||||
@ -254,11 +255,11 @@ async function consturctServer(moduleDefs) {
|
||||
if (song.freeTrialInfo !== null || !song.url || [1, 4].includes(song.fee)) {
|
||||
const match = require('@unblockneteasemusic/server')
|
||||
const source = process.env.UNBLOCK_SOURCE ? process.env.UNBLOCK_SOURCE.split(',') : ['pyncmd', 'kuwo', 'qq', 'migu', 'kugou']
|
||||
console.log("开始解灰", source)
|
||||
logger.info("开始解灰", source)
|
||||
const { url } = await match(req.query.id, source)
|
||||
song.url = url
|
||||
song.freeTrialInfo = 'null'
|
||||
console.log("解灰成功!")
|
||||
logger.info("解灰成功!")
|
||||
}
|
||||
if (song.url.includes('kuwo')) {
|
||||
const proxy = process.env.PROXY_URL;
|
||||
@ -285,7 +286,7 @@ async function consturctServer(moduleDefs) {
|
||||
}
|
||||
res.status(moduleResponse.status).send(moduleResponse.body)
|
||||
} catch (/** @type {*} */ moduleResponse) {
|
||||
console.log('[ERR]', decode(req.originalUrl), {
|
||||
logger.error(`${decode(req.originalUrl)}`, {
|
||||
status: moduleResponse.status,
|
||||
body: moduleResponse.body,
|
||||
})
|
||||
@ -324,7 +325,7 @@ async function serveNcmApi(options) {
|
||||
options.checkVersion &&
|
||||
checkVersion().then(({ npmVersion, ourVersion, status }) => {
|
||||
if (status == VERSION_CHECK_RESULT.NOT_LATEST) {
|
||||
console.log(
|
||||
logger.info(
|
||||
`最新版本: ${npmVersion}, 当前版本: ${ourVersion}, 请及时更新`,
|
||||
)
|
||||
}
|
||||
@ -339,9 +340,25 @@ async function serveNcmApi(options) {
|
||||
/** @type {import('express').Express & ExpressExtension} */
|
||||
const appExt = app
|
||||
appExt.server = app.listen(port, host, () => {
|
||||
console.log(`server running @ http://${host ? host : 'localhost'}:${port}`)
|
||||
console.log(`
|
||||
_ _ _____ __ __ _ ____ ___
|
||||
| \\ | |/ ____| \\/ | /\\ | | | _ \\_ |
|
||||
| \\| | | | \\ / | / \\ | | | |_) | |
|
||||
| . \` | | | |\\/| | / /\\ \\ | | | __/| |
|
||||
| |\\ | |____| | | | / ____ \\| |__| | | |
|
||||
|_| \\_|\\_____|_| |_| /_/ \\_\\____|_| |_|
|
||||
`)
|
||||
console.log(`
|
||||
╔═╗╔═╗╦ ╔═╗╔╗╔╦ ╦╔═╗╔╗╔╔═╗╔═╗╔╦╗
|
||||
╠═╣╠═╝║ ║╣ ║║║╠═╣╠═╣║║║║ ║╣ ║║
|
||||
╩ ╩╩ ╩═╝ ╚═╝╝╚╝╩ ╩╩ ╩╝╚╝╚═╝╚═╝═╩╝
|
||||
`)
|
||||
logger.info(`
|
||||
- Server started successfully @ http://${host ? host : 'localhost'}:${port}
|
||||
- Environment: ${process.env.NODE_ENV || 'development'}
|
||||
- Node Version: ${process.version}
|
||||
- Process ID: ${process.pid}`)
|
||||
})
|
||||
|
||||
return appExt
|
||||
}
|
||||
|
||||
|
@ -1,2 +0,0 @@
|
||||
const source = process.env.UNBLOCK_SOURCE ? process.env.UNBLOCK_SOURCE.split(',') : ['pyncmd', 'kuwo', 'qq', 'migu', 'kugou']
|
||||
console.log("开始解灰", source)
|
@ -1,5 +1,6 @@
|
||||
var url = require('url')
|
||||
var MemoryCache = require('./memory-cache')
|
||||
const logger = require('./logger.js')
|
||||
|
||||
var t = {
|
||||
ms: 1,
|
||||
@ -349,7 +350,7 @@ function ApiCache() {
|
||||
try {
|
||||
redis.del(key)
|
||||
} catch (err) {
|
||||
console.log('[apicache] error in redis.del("' + key + '")')
|
||||
logger.info('[apicache] error in redis.del("' + key + '")')
|
||||
}
|
||||
}
|
||||
index.all = index.all.filter(doesntMatch(key))
|
||||
@ -373,7 +374,7 @@ function ApiCache() {
|
||||
try {
|
||||
redis.del(target)
|
||||
} catch (err) {
|
||||
console.log('[apicache] error in redis.del("' + target + '")')
|
||||
logger.info('[apicache] error in redis.del("' + target + '")')
|
||||
}
|
||||
}
|
||||
|
||||
@ -404,7 +405,7 @@ function ApiCache() {
|
||||
try {
|
||||
redis.del(key)
|
||||
} catch (err) {
|
||||
console.log('[apicache] error in redis.del("' + key + '")')
|
||||
logger.info('[apicache] error in redis.del("' + key + '")')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
29
util/logger.js
Normal file
29
util/logger.js
Normal file
@ -0,0 +1,29 @@
|
||||
// ANSI 颜色代码
|
||||
const colors = {
|
||||
reset: '\x1b[0m',
|
||||
bright: '\x1b[1m',
|
||||
dim: '\x1b[2m',
|
||||
black: '\x1b[30m',
|
||||
red: '\x1b[31m',
|
||||
green: '\x1b[32m',
|
||||
yellow: '\x1b[33m',
|
||||
blue: '\x1b[34m',
|
||||
magenta: '\x1b[35m',
|
||||
cyan: '\x1b[36m',
|
||||
white: '\x1b[37m',
|
||||
bgRed: '\x1b[41m',
|
||||
bgGreen: '\x1b[42m',
|
||||
bgYellow: '\x1b[43m'
|
||||
};
|
||||
|
||||
const logger = {
|
||||
debug: (msg, ...args) => console.info(`${colors.cyan}[DEBUG]${colors.reset}`, msg, ...args),
|
||||
info: (msg, ...args) => console.info(`${colors.green}[INFO]${colors.reset}`, msg, ...args),
|
||||
warn: (msg, ...args) => console.info(`${colors.yellow}[WARN]${colors.reset}`, msg, ...args),
|
||||
error: (msg, ...args) => console.error(`${colors.red}[ERROR]${colors.reset}`, msg, ...args),
|
||||
success: (msg, ...args) => console.log(`${colors.bright}${colors.green}[SUCCESS]${colors.reset}`, msg, ...args),
|
||||
critical: (msg, ...args) => console.error(`${colors.bright}${colors.bgRed}[CRITICAL]${colors.reset}`, msg, ...args)
|
||||
};
|
||||
|
||||
// 导出logger
|
||||
module.exports = logger;
|
@ -15,6 +15,7 @@ const anonymous_token = fs.readFileSync(
|
||||
)
|
||||
const { URLSearchParams, URL } = require('url')
|
||||
const { APP_CONF } = require('../util/config.json')
|
||||
const logger = require('./logger.js')
|
||||
// request.debug = true // 开启可看到更详细信息
|
||||
|
||||
const WNMCID = (function () {
|
||||
@ -76,7 +77,7 @@ const createRequest = (uri, data, options) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let headers = options.headers || {}
|
||||
let ip = options.realIP || options.ip || ''
|
||||
// console.log(ip)
|
||||
// logger.info(ip)
|
||||
if (ip) {
|
||||
headers['X-Real-IP'] = ip
|
||||
headers['X-Forwarded-For'] = ip
|
||||
@ -201,11 +202,11 @@ const createRequest = (uri, data, options) => {
|
||||
|
||||
default:
|
||||
// 未知的加密方式
|
||||
console.log('[ERR]', 'Unknown Crypto:', crypto)
|
||||
logger.info('[ERR]', 'Unknown Crypto:', crypto)
|
||||
break
|
||||
}
|
||||
const answer = { status: 500, body: {}, cookie: [] }
|
||||
// console.log(headers, 'headers')
|
||||
// logger.info(headers, 'headers')
|
||||
let settings = {
|
||||
method: 'POST',
|
||||
url: url,
|
||||
@ -282,7 +283,7 @@ const createRequest = (uri, data, options) => {
|
||||
answer.status = 200
|
||||
}
|
||||
} catch (e) {
|
||||
// console.log(e)
|
||||
// logger.info(e)
|
||||
// can't decrypt and can't parse directly
|
||||
answer.body = body
|
||||
answer.status = res.status
|
||||
|
Loading…
x
Reference in New Issue
Block a user