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

又是我😂 #351

Merged
merged 6 commits into from
Oct 21, 2018
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
156 changes: 64 additions & 92 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,114 +1,86 @@
const express = require('express')
const apicache = require('apicache')
const path = require('path')
const fs = require('fs')
const app = express()
let cache = apicache.middleware
const { exec } = require('child_process');
exec('npm info NeteaseCloudMusicApi version', (err, stdout, stderr) => {
if (err) {
console.error(err);
return;
}
const onlinePackageVersion = stdout.trim();
const package = require('./package.json')
if (package.version < onlinePackageVersion) {
console.log(
'最新版:Version:' +
onlinePackageVersion +
',当前版本:' +
package.version +
',请及时更新'
)
}
})
const path = require('path')
const express = require('express')
const request = require('./util/request')
const package = require('./package.json')
const exec = require('child_process').exec
const cache = require('apicache').middleware

// 跨域设置
app.all('*', function(req, res, next) {
if (req.path !== '/' && !req.path.includes('.')) {
res.header('Access-Control-Allow-Credentials', true)
// 这里获取 origin 请求头 而不是用 *
res.header('Access-Control-Allow-Origin', req.headers['origin'] || '*')
res.header('Access-Control-Allow-Headers', 'X-Requested-With')
res.header('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS')
res.header('Content-Type', 'application/json;charset=utf-8')
}
next()
// version check
exec('npm info NeteaseCloudMusicApi version', (err, stdout, stderr) => {
if(!err){
let version = stdout.trim()
if(package.version < version){
console.log(`最新版本: ${version}, 当前版本: ${package.version}, 请及时更新`)
}
}
})

const onlyStatus200 = (req, res) => res.statusCode === 200

app.use(cache('2 minutes', onlyStatus200))

app.use(express.static(path.resolve(__dirname, 'public')))
const app = express()

// 补全缺失的cookie
const { completeCookie } = require('./util/init')
app.use(function(req, res, next) {
let cookie = completeCookie(req.headers.cookie)
req.headers.cookie = cookie.map(x => x[0]).concat(req.headers.cookie || []).join('; ')
res.append('Set-Cookie', cookie.map(x => (x.concat('Path=/').join('; '))))
next()
// CORS
app.use((req, res, next) => {
if(req.path !== '/' && !req.path.includes('.')){
res.header({
'Access-Control-Allow-Credentials': true,
'Access-Control-Allow-Origin': req.headers.origin || '*',
'Access-Control-Allow-Headers': 'X-Requested-With',
'Access-Control-Allow-Methods': 'PUT,POST,GET,DELETE,OPTIONS',
'Content-Type': 'application/json; charset=utf-8'
})
}
next()
})

// cookie parser
app.use(function(req, res, next) {
req.cookies = {}, (req.headers.cookie || '').split(/\s*;\s*/).forEach(pair => {
let crack = pair.indexOf('=')
if(crack < 1 || crack == pair.length - 1) return
req.cookies[decodeURIComponent(pair.slice(0, crack)).trim()] = decodeURIComponent(pair.slice(crack + 1)).trim()
})
next()
app.use((req, res, next) => {
req.cookies = {}, (req.headers.cookie || '').split(/\s*;\s*/).forEach(pair => {
let crack = pair.indexOf('=')
if(crack < 1 || crack == pair.length - 1) return
req.cookies[decodeURIComponent(pair.slice(0, crack)).trim()] = decodeURIComponent(pair.slice(crack + 1)).trim()
})
next()
})

app.use(function(req, res, next) {
const proxy = req.query.proxy
if (proxy) {
req.headers.cookie += `__proxy__${proxy}`
}
next()
})
// cache
app.use(cache('2 minutes', ((req, res) => res.statusCode === 200)))

// 因为这几个文件对外所注册的路由 和 其他文件对外注册的路由规则不一样, 所以专门写个MAP对这些文件做特殊处理
const UnusualRouteFileMap = {
// key 为文件名, value 为对外注册的路由
'daily_signin.js': '/daily_signin',
'fm_trash.js': '/fm_trash',
'personal_fm.js': '/personal_fm'
}
// static
app.use(express.static(path.join(__dirname, 'public')))

// router
const special = {
'daily_signin.js': '/daily_signin',
'fm_trash.js': '/fm_trash',
'personal_fm.js': '/personal_fm'
}

// 改写router为module
const requestMod = require('./util/request')
let dev = express()
fs.readdirSync(path.join(__dirname, 'module'))
.reverse()
.forEach(file => {
if (!(/\.js$/i.test(file))) return
let route = (file in UnusualRouteFileMap) ? UnusualRouteFileMap[file] : '/' + file.replace(/\.js$/i, '').replace(/_/g, '/')
let question = require(path.join(__dirname, 'module', file))

dev.use(route, (req, res) => {
let query = {...req.query, cookie: req.cookies}
question(query, requestMod)
.then(answer => {
console.log('[OK]', decodeURIComponent(req.originalUrl))
res.append('Set-Cookie', answer.cookie)
res.status(answer.status).send(answer.body)
})
.catch(answer => {
console.log('[ERR]', decodeURIComponent(req.originalUrl))
res.append('Set-Cookie', answer.cookie)
res.status(answer.status).send(answer.body)
fs.readdirSync(path.join(__dirname, 'module')).reverse().forEach(file => {
if(!(/\.js$/i.test(file))) return
let route = (file in special) ? special[file] : '/' + file.replace(/\.js$/i, '').replace(/_/g, '/')
let question = require(path.join(__dirname, 'module', file))

app.use(route, (req, res) => {
let query = {...req.query, ...req.body, cookie: req.cookies}
question(query, request)
.then(answer => {
console.log('[OK]', decodeURIComponent(req.originalUrl))
res.append('Set-Cookie', answer.cookie)
res.status(answer.status).send(answer.body)
})
.catch(answer => {
console.log('[ERR]', decodeURIComponent(req.originalUrl))
if(answer.body.code =='301') answer.body.msg = '需要登录'
res.append('Set-Cookie', answer.cookie)
res.status(answer.status).send(answer.body)
})
})
})
})
app.use('/', dev)

const port = process.env.PORT || 3000

app.server = app.listen(port, () => {
console.log(`server running @ http://localhost:${port}`)
console.log(`server running @ http://localhost:${port}`)
})

module.exports = app
2 changes: 1 addition & 1 deletion module/artist_sublist.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// 我的歌手列表
// 关注歌手列表

module.exports = (query, request) => {
const data = {
Expand Down
16 changes: 2 additions & 14 deletions module/banner.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,7 @@

module.exports = (query, request) => {
return request(
'GET', `http://music.163.com/discover`, {},
{ua: 'pc', proxy: query.proxy}
'POST', `http://music.163.com/api/v2/banner/get`, {clientType: "pc"},
{crypto: 'linuxapi', proxy: query.proxy}
)
.then(response => {
try{
const banners = eval(`(${/Gbanners\s*=\s*([^;]+);/.exec(response.body)[1]})`)
response.body = {code: 200, banners: banners}
return response
}
catch(err){
response.status = 500
response.body = {code: 500, msg: err.stack}
return Promise.reject(response)
}
})
}
12 changes: 8 additions & 4 deletions module/check_music.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ module.exports = (query, request) => {
{crypto: 'weapi', cookie: query.cookie, proxy: query.proxy}
)
.then(response => {
if (response.body.code == 200) {
if (response.body.data[0].code == 200){
response.body = {success: true, message: 'ok'}
return response
let playable = false
if(response.body.code == 200){
if(response.body.data[0].code == 200){
playable = true
}
}
if(playable){
response.body = {success: true, message: 'ok'}
return response
}
else{
response.status = 404
response.body = {success: false, message: '亲爱的,暂无版权'}
Expand Down
2 changes: 1 addition & 1 deletion module/dj_sublist.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// 我的电台列表
// 订阅电台列表

module.exports = (query, request) => {
const data = {
Expand Down
2 changes: 1 addition & 1 deletion module/like.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// 红心取消红心歌曲
// 红心与取消红心歌曲

module.exports = (query, request) => {
query.like = (query.like ? true : false)
Expand Down
2 changes: 2 additions & 0 deletions module/mv_url.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// MV链接

module.exports = (query, request) => {
const data = {
id: query.id,
Expand Down
1 change: 1 addition & 0 deletions module/send_playlist.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 私信歌单

module.exports = (query, request) => {
query.cookie.os = 'pc'
const data = {
id: query.playlist,
type: 'playlist',
Expand Down
1 change: 1 addition & 0 deletions module/send_text.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 私信

module.exports = (query, request) => {
query.cookie.os = 'pc'
const data = {
id: query.playlist,
type: 'text',
Expand Down
10 changes: 8 additions & 2 deletions module/top_song.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
// 最新单曲(暂时废弃?)
// 新歌速递

module.exports = (query, request) => {
const data = {
areaId: query.type || 0, // 全部:0 华语:7 欧美:96 日本:8 韩国:16
limit: query.limit || 100,
offset: query.offset || 0,
total: true
}
return request(
'POST', `http://music.163.com/weapi/v1/discovery/new/songs`, {},
'POST', `http://music.163.com/weapi/v1/discovery/new/songs`, data,
{crypto: 'weapi', cookie: query.cookie, proxy: query.proxy}
)
}
2 changes: 1 addition & 1 deletion module/user_cloud_search.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// 云盘数据详情?(暂时不要使用)
// 云盘数据详情(暂时不要使用)

module.exports = (query, request) => {
const data = {
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
"license": "MIT",
"dependencies": {
"apicache": "^1.2.1",
"big-integer": "^1.6.28",
"express": "^4.16.3",
"request": "^2.85.0"
},
Expand All @@ -25,4 +24,4 @@
"mocha": "^5.1.1",
"power-assert": "^1.5.0"
}
}
}
83 changes: 25 additions & 58 deletions util/crypto.js
Original file line number Diff line number Diff line change
@@ -1,67 +1,34 @@
// 参考 https://github.com/darknessomi/musicbox/wiki/
'use strict'
const crypto = require('crypto')
const bigInt = require('big-integer')
const modulus =
'00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
const nonce = '0CoJUm6Qyw8W8jud'
const pubKey = '010001'

String.prototype.hexEncode = function() {
let hex, i

let result = ''
for (i = 0; i < this.length; i++) {
hex = this.charCodeAt(i).toString(16)
result += ('' + hex).slice(-4)
}
return result
}

function createSecretKey(size) {
const keys = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
let key = ''
for (let i = 0; i < size; i++) {
let pos = Math.random() * keys.length
pos = Math.floor(pos)
key = key + keys.charAt(pos)
}
return key
}

function aesEncrypt(text, secKey) {
const _text = text
const lv = new Buffer('0102030405060708', 'binary')
const _secKey = new Buffer(secKey, 'binary')
const cipher = crypto.createCipheriv('AES-128-CBC', _secKey, lv)
let encrypted = cipher.update(_text, 'utf8', 'base64')
encrypted += cipher.final('base64')
return encrypted
const iv = Buffer.from('0102030405060708')
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 aesEncrypt = (buffer, mode, key, iv) => {
const cipher = crypto.createCipheriv('aes-128-' + mode, key, iv)
return Buffer.concat([cipher.update(buffer),cipher.final()])
}

function zfill(str, size) {
while (str.length < size) str = '0' + str
return str
const rsaEncrypt = (buffer, key) => {
buffer = Buffer.concat([Buffer.alloc(128 - buffer.length), buffer])
return crypto.publicEncrypt({key: key, padding: crypto.constants.RSA_NO_PADDING}, buffer)
}

function rsaEncrypt(text, pubKey, modulus) {
const _text = text.split('').reverse().join('')
const biText = bigInt(new Buffer(_text).toString('hex'), 16),
biEx = bigInt(pubKey, 16),
biMod = bigInt(modulus, 16),
biRet = biText.modPow(biEx, biMod)
return zfill(biRet.toString(16), 256)
const weapi = (object) => {
const text = JSON.stringify(object)
const secretKey = crypto.randomBytes(16).map(n => (base62.charAt(n % 62).charCodeAt()))
return {
params: aesEncrypt(Buffer.from(aesEncrypt(Buffer.from(text), 'cbc', presetKey, iv).toString('base64')), 'cbc', secretKey, iv).toString('base64'),
encSecKey: rsaEncrypt(secretKey.reverse(), publicKey).toString('hex')
}
}

function Encrypt(obj) {
const text = JSON.stringify(obj)
const secKey = createSecretKey(16)
const encText = aesEncrypt(aesEncrypt(text, nonce), secKey)
const encSecKey = rsaEncrypt(secKey, pubKey, modulus)
return {
params: encText,
encSecKey: encSecKey
}
const linuxapi = (object) => {
const text = JSON.stringify(object)
return {
eparams: aesEncrypt(Buffer.from(text), 'ecb', linuxapiKey, '').toString('hex').toUpperCase()
}
}

module.exports = Encrypt
module.exports = {weapi, linuxapi}
Loading