mirror of
https://gitlab.com/Binaryify/neteasecloudmusicapi.git
synced 2025-05-23 22:37:41 +08:00
feat: 新增 eapi 解析接口和使用 demo
This commit is contained in:
parent
1c1c850b51
commit
15a2e1bcdf
13
CHANGELOG.MD
13
CHANGELOG.MD
@ -1,25 +1,36 @@
|
||||
# 更新日志
|
||||
|
||||
### 4.19.0 | 2024.05.07
|
||||
|
||||
- 云贝签到接口替换
|
||||
- 新增 eapi 解析接口和使用 demo
|
||||
|
||||
### 4.18.3 | 2024.05.07
|
||||
|
||||
- 云盘上传问题修复
|
||||
|
||||
### 4.18.2 | 2024.04.30
|
||||
|
||||
- 配置和 typo 修复
|
||||
|
||||
### 4.18.0 | 2024.04.30
|
||||
|
||||
- 补充游客 deviceid,防止都指向相同账户,避免网络拥堵提示问题
|
||||
- ip 设置问题修复
|
||||
- 新增`电台排行榜获取`, `获取声音歌词`接口
|
||||
|
||||
### 4.17.1 | 2024.04.29
|
||||
|
||||
- song/url 参数问题修复
|
||||
|
||||
### 4.17.0 | 2024.04.29
|
||||
|
||||
- ua 更新
|
||||
- cookie 处理优化
|
||||
- 收藏与取消收藏歌单接口更新
|
||||
|
||||
|
||||
### 4.16.6 | 2024.04.25
|
||||
|
||||
- ua 问题修复
|
||||
- 使用 crypto-js 重构 crypto 部分
|
||||
|
||||
|
26
module/eapi_decrypt.js
Normal file
26
module/eapi_decrypt.js
Normal file
@ -0,0 +1,26 @@
|
||||
const { eapiResDecrypt, eapiReqDecrypt } = require('../util/crypto')
|
||||
|
||||
module.exports = async (query, request) => {
|
||||
const hexString = query.hexString
|
||||
const isFormat = query.isFormat != 'false'
|
||||
if (!hexString) {
|
||||
return {
|
||||
status: 400,
|
||||
body: {
|
||||
code: 400,
|
||||
message: 'hex string is required',
|
||||
},
|
||||
}
|
||||
}
|
||||
// 去除空格
|
||||
let pureHexString = hexString.replace(/\s/g, '')
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
code: 200,
|
||||
data: isFormat
|
||||
? eapiReqDecrypt(pureHexString)
|
||||
: eapiResDecrypt(pureHexString),
|
||||
},
|
||||
}
|
||||
}
|
@ -1,12 +1,15 @@
|
||||
module.exports = (query, request) => {
|
||||
const data = {
|
||||
type: '0',
|
||||
}
|
||||
return request('POST', `https://music.163.com/api/point/dailyTask`, data, {
|
||||
const data = {}
|
||||
return request(
|
||||
'POST',
|
||||
`https://music.163.com/api/pointmall/user/sign`,
|
||||
data,
|
||||
{
|
||||
crypto: 'weapi',
|
||||
cookie: query.cookie,
|
||||
ua: query.ua || '',
|
||||
proxy: query.proxy,
|
||||
realIP: query.realIP,
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
15
module_example/eapi_decrypt.js
Normal file
15
module_example/eapi_decrypt.js
Normal file
@ -0,0 +1,15 @@
|
||||
const { eapiResDecrypt, eapiReqDecrypt } = require('../util/crypto')
|
||||
|
||||
let reqParamsHex =
|
||||
'6E0B1C712DCB3648589D7C950C296204072A88C3E080C4CFFD0A71A553EC2533BA88E11B1E1C6DF3BE8EFA26177FCB6FCA34EB3FAFAB4671B2BBAFA9781AFDA2BF53A3DC423722493837B9BC6E80CED5BBD2DDC2856920E4D4E3E7F3EB77ECF265217A66AE677BE36F2D6FB203F721CA250E1453EA61A34904E33D5FCB9D483601D744BE0AE979AC911A00F25828538844F4B1C24F6C34880A4AB257F530C7FB10A81FED32B18D09D70C0B1B9D34A2E58A3C3FAD382C6F958077059C4F801AD7B3B248FDB9D7A59B6A9EEFF8C781A84315B33A7AFD48BE9FCFCBE1902CCC27949ACF2BDE3FA34D116E230C3597E8320B8C42BBBF371A00C03EC428E0440EB94C1540F3FD4173D29E310AFE43AB0EF449852904103EF305FC435AD43B7D8673642F74C89CCB2F1A6A79B3BE14F1235D3843C3B241D12C05DBDDF37B68CA8B5D0230AF1FCF2A9705886F4D126B33FFF6948DE1E4046DB6423D687E96C5B65122464D2E71AEC7722935FF2C3796FAE253A16AA3B102FBE7296AB0DB9EA5C46AD12B'.replaceAll(
|
||||
' ',
|
||||
'',
|
||||
)
|
||||
const resHex =
|
||||
`FB A2 DB 80 DF 26 69 F1 AA 14 81 8E 56 93 53 A6 44 9C 10 70 26 65 59 64 AE 09 02 75 56 65 1C 98 E6 DC E7 50 F1 6E 64 78 BC 87 B8 0E 6D 52 B0 23`.replaceAll(
|
||||
' ',
|
||||
'',
|
||||
)
|
||||
console.log(reqParamsHex)
|
||||
console.log(eapiReqDecrypt(reqParamsHex))
|
||||
console.log(eapiResDecrypt(resHex))
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "NeteaseCloudMusicApi",
|
||||
"version": "4.18.3",
|
||||
"version": "4.19.0",
|
||||
"description": "网易云音乐 NodeJS 版 API",
|
||||
"scripts": {
|
||||
"start": "node app.js",
|
||||
|
64
public/eapi_decrypt.html
Normal file
64
public/eapi_decrypt.html
Normal file
@ -0,0 +1,64 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>eapi 参数和返回内容解析</title>
|
||||
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app" class="p-5 flex flex-col">
|
||||
<h1 class="text-2xl font-bold mb-5">eapi 参数和返回内容解析</h1>
|
||||
<textarea class="border border-gray-300 p-3 mb-5" v-model="hexString" rows="10"></textarea>
|
||||
<button @click="decrypt" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||
解密
|
||||
</button>
|
||||
<div class="mt-3">
|
||||
<input type="radio" id="format" name="format" v-model="isFormat" value="true">
|
||||
<label for="format" class="ml-2">格式化(针对请求数据的 params)</label>
|
||||
<input type="radio" id="noFormat" name="format" v-model="isFormat" value="false" class="ml-5">
|
||||
<label for="noFormat" class="ml-2">不格式化(针对返回内容解析)</label>
|
||||
</div>
|
||||
<div>
|
||||
<p>解密结果: {{ result }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@3"></script>
|
||||
|
||||
<script>
|
||||
const app = Vue.createApp({
|
||||
data() {
|
||||
return {
|
||||
hexString: 'AD96DDB984491E79B6F429DD650C6E2AE524627AC223AC9A123C66BB0997965950FED137544A93DFC718E16F57C8C121AF537086F395570A5602A3922366D11964DAFACD7830AACABF62E5650E67F457E79C1D2E13502391FC3487216CC5BF8681843FCB8E05559487EB18AAC1BE0EFEA4F7B6A050478366153A9426C238B8869600B275704555A9EB94C92E4F3FDABE9E0BCE07645410D0AA7B675698A4CAE6CD3620633ABF0B849A4244CC8DFC5DB2646D5EA9B3954E62BFEF19AFEAFDDC34E55C3E9A1DD3167CF53D443617108141',
|
||||
result: '',
|
||||
isFormat: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.decrypt()
|
||||
},
|
||||
methods: {
|
||||
async decrypt() {
|
||||
try {
|
||||
const res = await axios({
|
||||
url: `/eapi/decrypt?hexString=${this.hexString}&isFormat=${this.isFormat}`,
|
||||
method: 'post'
|
||||
})
|
||||
this.result = res.data
|
||||
console.log(res.data);
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
alert(error.response.data.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
app.mount('#app')
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -19,6 +19,7 @@
|
||||
<li>3. <a href="./dj/program?rid=336355127">电台节目</a></li>
|
||||
<li>4. <a href="./qrlogin.html">二维码登录</a></li>
|
||||
<li>4. <a href="./audio_match_demo/index.html">听歌识曲</a></li>
|
||||
<li>5. <a href="./eapi_decrypt.html">eapi 参数和返回内容解析</a></li>
|
||||
</ul>
|
||||
<style>
|
||||
html,
|
||||
|
@ -25,7 +25,27 @@ const aesEncrypt = (text, mode, key, iv, format = 'base64') => {
|
||||
|
||||
return encrypted.ciphertext.toString().toUpperCase()
|
||||
}
|
||||
|
||||
const aesDecrypt = (ciphertext, key, iv, format = 'base64') => {
|
||||
let bytes
|
||||
if (format === 'base64') {
|
||||
bytes = CryptoJS.AES.decrypt(ciphertext, CryptoJS.enc.Utf8.parse(key), {
|
||||
iv: CryptoJS.enc.Utf8.parse(iv),
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
})
|
||||
} else {
|
||||
bytes = CryptoJS.AES.decrypt(
|
||||
{ ciphertext: CryptoJS.enc.Hex.parse(ciphertext) },
|
||||
CryptoJS.enc.Utf8.parse(key),
|
||||
{
|
||||
iv: CryptoJS.enc.Utf8.parse(iv),
|
||||
mode: CryptoJS.mode.ECB,
|
||||
padding: CryptoJS.pad.Pkcs7,
|
||||
},
|
||||
)
|
||||
}
|
||||
return bytes.toString(CryptoJS.enc.Utf8)
|
||||
}
|
||||
const rsaEncrypt = (str, key) => {
|
||||
const forgePublicKey = forge.pki.publicKeyFromPem(key)
|
||||
const encrypted = forgePublicKey.encrypt(str, 'NONE')
|
||||
@ -65,7 +85,25 @@ const eapi = (url, object) => {
|
||||
params: aesEncrypt(data, 'ecb', eapiKey, '', 'hex'),
|
||||
}
|
||||
}
|
||||
const eapiResDecrypt = (encryptedParams) => {
|
||||
// 使用aesDecrypt解密参数
|
||||
const decryptedData = aesDecrypt(encryptedParams, eapiKey, '', 'hex')
|
||||
return decryptedData
|
||||
}
|
||||
const eapiReqDecrypt = (encryptedParams) => {
|
||||
// 使用aesDecrypt解密参数
|
||||
const decryptedData = aesDecrypt(encryptedParams, eapiKey, '', 'hex')
|
||||
// 使用正则表达式解析出URL和数据
|
||||
const match = decryptedData.match(/(.*?)-36cd479b6b5-(.*?)-36cd479b6b5-(.*)/)
|
||||
if (match) {
|
||||
const url = match[1]
|
||||
const data = JSON.parse(match[2])
|
||||
return { url, data }
|
||||
}
|
||||
|
||||
// 如果没有匹配到,返回null
|
||||
return null
|
||||
}
|
||||
const decrypt = (cipher) => {
|
||||
const decipher = CryptoJS.AES.decrypt(
|
||||
{
|
||||
@ -80,4 +118,13 @@ const decrypt = (cipher) => {
|
||||
return decryptedBytes
|
||||
}
|
||||
|
||||
module.exports = { weapi, linuxapi, eapi, decrypt, aesEncrypt }
|
||||
module.exports = {
|
||||
weapi,
|
||||
linuxapi,
|
||||
eapi,
|
||||
decrypt,
|
||||
aesEncrypt,
|
||||
aesDecrypt,
|
||||
eapiReqDecrypt,
|
||||
eapiResDecrypt,
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user