Skip to content
This repository has been archived by the owner on Apr 16, 2024. It is now read-only.

新增eapi算法 #491

Merged
merged 5 commits into from
May 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ $ sudo docker run -d -p 3000:3000 netease-music-api
!> 文档可能会有缓存 , 如果文档版本和 github 上的版本不一致,请清除缓存再查看

!> 由于网易限制,此项目在国外服务器上使用会受到限制,如需解决 , 可使用大陆服务器或者使用代理 , 感谢 [@hiyangguo](https://github.com/hiyangguo)提出的[解决方法](https://github.com/Binaryify/NeteaseCloudMusicApi/issues/29#issuecomment-298358438):
在 'util.js' 的 'headers' 处增加 `X-Real-IP':'211.161.244.70' // 任意国内 IP`
在 '/util/request.js' 的 'headers' 处增加 `X-Real-IP':'211.161.244.70' // 任意国内 IP`
即可解决

### 登录
Expand Down Expand Up @@ -558,9 +558,9 @@ tags:歌单tag

`offset`: 偏移数量 , 用于分页 , 如 :( 评论页数 -1)\*20, 其中 20 为 limit 的值

**接口地址 :** `/act/hot`
**接口地址 :** `/hot/topic`

**调用例子 :** `/act/hot?limit=30&offset=30`
**调用例子 :** `/hot/topic?limit=30&offset=30`

### 心动模式/智能播放
说明 : 调用此接口 , 可获取心动模式/智能播放列表
Expand Down
19 changes: 14 additions & 5 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta name="referrer" content="never">
<link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/vue.css">
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<script>
(adsbygoogle = window.adsbygoogle || []).push({
google_ad_client: "ca-pub-5159844745975514",
enable_page_level_ads: true
});
</script>
</head>
<body>
<div id="app"></div>
Expand All @@ -27,11 +34,13 @@
navigator.serviceWorker.register('sw.js')
}
</script>
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-139996012-1"></script>
<script>
(adsbygoogle = window.adsbygoogle || []).push({
google_ad_client: "ca-pub-5159844745975514",
enable_page_level_ads: true
});
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());

gtag('config', 'UA-139996012-1');
</script>
</html>
14 changes: 14 additions & 0 deletions module/batch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = (query, request) => {
const data = {
"e_r": true
};
Object.keys(query).forEach(i => {
if (/^\/api\//.test(i)) {
data[i] = query[i]
}
})
return request(
'POST', `http://music.163.com/eapi/batch`, data,
{crypto: 'eapi', proxy: query.proxy, url: '/api/batch', cookie: query.cookie}
)
};
18 changes: 17 additions & 1 deletion util/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const presetKey = Buffer.from('0CoJUm6Qyw8W8jud')
const linuxapiKey = Buffer.from('rFgB&h#%2?^eDg:Q')
const base62 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
const publicKey = '-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDgtQn2JZ34ZC28NWYpAUd98iZ37BUrX/aKzmFbt7clFSs6sXqHauqKWqdtLkF2KexO40H1YTX8z2lSgBBOAxLsvaklV8k4cBFK9snQXE9/DDaFt6Rr7iVZMldczhC0JNgTz+SHXT6CBHuX3e9SdB1Ua44oncaTWz7OBGLbCiK45wIDAQAB\n-----END PUBLIC KEY-----'
const eapiKey = 'e82ckenh8dichen8'

const aesEncrypt = (buffer, mode, key, iv) => {
const cipher = crypto.createCipheriv('aes-128-' + mode, key, iv)
Expand Down Expand Up @@ -31,4 +32,19 @@ const linuxapi = (object) => {
}
}

module.exports = {weapi, linuxapi}
const eapi = (url, object) => {
const text = typeof object === 'object' ? JSON.stringify(object) : object;
const message = `nobody${url}use${text}md5forencrypt`
const digest = crypto.createHash('md5').update(message).digest('hex')
const data = `${url}-36cd479b6b5-${text}-36cd479b6b5-${digest}`
return {
params: aesEncrypt(Buffer.from(data), 'ecb', eapiKey, '').toString('hex').toUpperCase()
}
}

const decrypt = cipherBuffer => {
const decipher = crypto.createDecipheriv('aes-128-ecb',eapiKey,'')
return Buffer.concat([decipher.update(cipherBuffer), decipher.final()])
}

module.exports = {weapi, linuxapi, eapi, decrypt}
75 changes: 68 additions & 7 deletions util/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const encrypt = require('./crypto')
const request = require('request')
const queryString = require('querystring')
const PacProxyAgent = require('pac-proxy-agent')
const zlib = require('zlib')

// request.debug = true // 开启可看到更详细信息

Expand All @@ -25,16 +26,16 @@ const chooseUserAgent = ua => {
let index = 0
if (typeof ua == 'undefined')
index = Math.floor(Math.random() * userAgentList.length)
else if (ua == 'mobile') index = Math.floor(Math.random() * 7)
else if (ua == 'pc') index = Math.floor(Math.random() * 5) + 8
else if (ua === 'mobile') index = Math.floor(Math.random() * 7)
else if (ua === 'pc') index = Math.floor(Math.random() * 5) + 8
else return ua
return userAgentList[index]
}

const createRequest = (method, url, data, options) => {
return new Promise((resolve, reject) => {
let headers = { 'User-Agent': chooseUserAgent(options.ua) }
if (method.toUpperCase() == 'POST')
if (method.toUpperCase() === 'POST')
headers['Content-Type'] = 'application/x-www-form-urlencoded'
if (url.includes('music.163.com'))
headers['Referer'] = 'https://music.163.com'
Expand All @@ -51,12 +52,12 @@ const createRequest = (method, url, data, options) => {
.join('; ')
else if (options.cookie) headers['Cookie'] = options.cookie

if (options.crypto == 'weapi') {
if (options.crypto === 'weapi') {
let csrfToken = (headers['Cookie'] || '').match(/_csrf=([^(;|$)]+)/)
data.csrf_token = csrfToken ? csrfToken[1] : ''
data = encrypt.weapi(data)
url = url.replace(/\w*api/, 'weapi')
} else if (options.crypto == 'linuxapi') {
} else if (options.crypto === 'linuxapi') {
data = encrypt.linuxapi({
method: method,
url: url.replace(/\w*api/, 'api'),
Expand All @@ -65,6 +66,35 @@ const createRequest = (method, url, data, options) => {
headers['User-Agent'] =
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36'
url = 'https://music.163.com/api/linux/forward'
} else if (options.crypto === 'eapi') {
const cookie = options.cookie || {};
const csrfToken = cookie['__csrf'] || ''
const header = {
"osver": cookie.osver, //系统版本
"deviceId": cookie.deviceId, //encrypt.base64.encode(imei + '\t02:00:00:00:00:00\t5106025eb79a5247\t70ffbaac7')
"appver": cookie.appver || "6.1.1", // app版本
"versioncode": cookie.versioncode || "140", //版本号
"mobilename": cookie.mobilename, //设备model
"buildver": cookie.buildver || Date.now().toString().substr(0, 10),
"resolution": cookie.resolution || "1920x1080", //设备分辨率
"__csrf": csrfToken,
"os": cookie.os || 'android',
"channel": cookie.channel,
"requestId":`${Date.now()}_${Math.floor(Math.random() * 1000).toString().padStart(4, '0')}`
}
if (cookie.MUSIC_U) header["MUSIC_U"] = cookie.MUSIC_U
if (cookie.MUSIC_A) header["MUSIC_A"] = cookie.MUSIC_A
headers['Cookie'] = Object.keys(header)
.map(
key =>
encodeURIComponent(key) +
'=' +
encodeURIComponent(header[key])
)
.join('; ')
data.header = header
data = encrypt.eapi(options.url, data)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

options.url 是什么鬼==

url = url.replace(/\w*api/,'eapi')
}

const answer = { status: 500, body: {}, cookie: [] }
Expand All @@ -75,6 +105,8 @@ const createRequest = (method, url, data, options) => {
body: queryString.stringify(data)
}

if (options.crypto === 'eapi') settings.encoding = null

if (/\.pac$/i.test(options.proxy)) {
settings.agent = new PacProxyAgent(options.proxy)
} else {
Expand All @@ -93,12 +125,41 @@ const createRequest = (method, url, data, options) => {
x.replace(/\s*Domain=[^(;|$)]+;*/, '')
)
try {
answer.body = JSON.parse(body)
answer.status = answer.body.code || res.statusCode
if (options.crypto === 'eapi') {

zlib.unzip(body, function (err, buffer) {
const _buffer = err ? body : buffer
try {
try{
answer.body = JSON.parse(encrypt.decrypt(_buffer).toString())
answer.status = answer.body.code || res.statusCode
} catch(e){
answer.body = JSON.parse(_buffer.toString())
answer.status = res.statusCode
}
} catch (e) {
answer.body = _buffer.toString()
answer.status = res.statusCode
}
answer.status =
100 < answer.status && answer.status < 600 ? answer.status : 400
if (answer.status === 200) resolve(answer)
else reject(answer)
});
return false

} else {

answer.body = JSON.parse(body)
answer.status = answer.body.code || res.statusCode

}

} catch (e) {
answer.body = body
answer.status = res.statusCode
}

answer.status =
100 < answer.status && answer.status < 600 ? answer.status : 400
if (answer.status == 200) resolve(answer)
Expand Down