New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

NeteaseCloudMusicApi

Package Overview
Dependencies
Maintainers
1
Versions
336
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

NeteaseCloudMusicApi - npm Package Compare versions

Comparing version 3.0.0 to 3.0.1

156

app.js

@@ -0,107 +1,79 @@

const fs = require('fs')
const path = require('path')
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');
const request = require('./util/request')
const package = require('./package.json')
const exec = require('child_process').exec
const cache = require('apicache').middleware
// version check
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 +
',请及时更新'
)
}
if(!err){
let version = stdout.trim()
if(package.version < version){
console.log(`最新版本: ${version}, 当前版本: ${package.version}, 请及时更新`)
}
}
})
// 跨域设置
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()
})
const app = express()
const onlyStatus200 = (req, res) => res.statusCode === 200
app.use(cache('2 minutes', onlyStatus200))
app.use(express.static(path.resolve(__dirname, 'public')))
// 补全缺失的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)
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)
})
})
.catch(answer => {
console.log('[ERR]', decodeURIComponent(req.originalUrl))
res.append('Set-Cookie', answer.cookie)
res.status(answer.status).send(answer.body)
})
})
})
app.use('/', dev)

@@ -111,5 +83,5 @@ 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
# 更新日志
### 3.0.1 | 2018.10.21
- 合并 PR([#351](https://github.com/Binaryify/NeteaseCloudMusicApi/pull/351))
- 文档增加 `/top/song` 接口
- `/banner` 换成 linux api,返回结构有所变动
- `/check/music` 已知 bug 修复
### 3.0.0 | 2018.10.14
## 整体
- 完善文档,增加之前没写进文档的接口说明

@@ -11,17 +23,18 @@

- 模块化, 剥离res,req, 方便导出调用
- 模块化, 剥离 res,req, 方便导出调用
- 增加 cookie-parser
- 增加 cookie-parser
### 参数修改
- `/song/detail` 增加多id支持
- `/toplist/detail` 移除参数
- `/song/detail` 增加多 id 支持
- `/resource/like` 增加参数 `type`
- `/toplist/detail` 移除参数
- `/resource/like` 增加参数 `type`
- `/top/playlist/highquality` 增加分页参数 `before`
### 统一参数
- `/artist/sub` 与 `artist/unsub` 合并, 用`query.t`

@@ -33,5 +46,6 @@

### URL重命名
- `/video` 改为 `video/url`
### URL 重命名
- `/video` 改为 `video/url`
- `/mv` 改为 `mv/detail`

@@ -42,2 +56,3 @@

### 转发逻辑修改
- `/toplist/artist` 换成 weapi

@@ -47,10 +62,12 @@

### BUG修复
### BUG 修复
- `/playlist/create`, `/playlist/update` 被判欺骗,增加 cookie
### 路由增删
- 删除 `/recommend/dislike`
- 增加 `/video/sub` (收藏视频), `/mv/sub` (收藏MV)
- 删除 `/recommend/dislike`
- 增加 `/video/sub` (收藏视频), `/mv/sub` (收藏 MV)
- 增加 `/video/detail` (视频详情)

@@ -61,4 +78,5 @@

### 2.20.5 | 2018.09.29
修复非法参数403 #335, 修复代理错误 #334
修复非法参数 403 #335, 修复代理错误 #334
### 2.20.4 | 2018.09.27

@@ -65,0 +83,0 @@

@@ -1,2 +0,2 @@

// 我的歌手列表
// 关注歌手列表

@@ -3,0 +3,0 @@ module.exports = (query, request) => {

@@ -5,17 +5,5 @@ // 首页轮播图

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)
}
})
}

@@ -13,8 +13,12 @@ // 歌曲可用性

.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{

@@ -21,0 +25,0 @@ response.status = 404

@@ -1,2 +0,2 @@

// 我的电台列表
// 订阅电台列表

@@ -3,0 +3,0 @@ module.exports = (query, request) => {

@@ -1,2 +0,2 @@

// 红心取消红心歌曲
// 红心与取消红心歌曲

@@ -3,0 +3,0 @@ module.exports = (query, request) => {

@@ -0,1 +1,3 @@

// MV链接
module.exports = (query, request) => {

@@ -2,0 +4,0 @@ const data = {

// 私信歌单
module.exports = (query, request) => {
query.cookie.os = 'pc'
const data = {

@@ -5,0 +6,0 @@ id: query.playlist,

// 私信
module.exports = (query, request) => {
query.cookie.os = 'pc'
const data = {

@@ -5,0 +6,0 @@ id: query.playlist,

@@ -1,8 +0,16 @@

// 最新单曲(暂时废弃?)
// 新歌速递
module.exports = (query, request) => {
return request(
'POST', `http://music.163.com/weapi/v1/discovery/new/songs`, {},
{crypto: 'weapi', cookie: query.cookie, proxy: query.proxy}
)
}
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`,
data,
{ crypto: 'weapi', cookie: query.cookie, proxy: query.proxy }
)
}

@@ -1,2 +0,2 @@

// 云盘数据详情?(暂时不要使用)
// 云盘数据详情(暂时不要使用)

@@ -3,0 +3,0 @@ module.exports = (query, request) => {

{
"name": "NeteaseCloudMusicApi",
"version": "3.0.0",
"version": "3.0.1",
"description": "网易云音乐 NodeJS 版 API",

@@ -19,3 +19,2 @@ "scripts": {

"apicache": "^1.2.1",
"big-integer": "^1.6.28",
"express": "^4.16.3",

@@ -29,2 +28,2 @@ "request": "^2.85.0"

}
}
}

@@ -1,67 +0,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'
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-----'
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
const aesEncrypt = (buffer, mode, key, iv) => {
const cipher = crypto.createCipheriv('aes-128-' + mode, key, iv)
return Buffer.concat([cipher.update(buffer),cipher.final()])
}
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
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 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 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 zfill(str, size) {
while (str.length < size) str = '0' + str
return str
const linuxapi = (object) => {
const text = JSON.stringify(object)
return {
eparams: aesEncrypt(Buffer.from(text), 'ecb', linuxapiKey, '').toString('hex').toUpperCase()
}
}
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)
}
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
}
}
module.exports = Encrypt
module.exports = {weapi, linuxapi}

@@ -1,8 +0,8 @@

const encrypt = require('./crypto.js')
const encrypt = require('./crypto')
const request = require('request')
const queryString = require('querystring')
// request.debug = false
request.debug = true
function chooseUserAgent(ua) {
const chooseUserAgent = (ua) => {
const userAgentList = [

@@ -36,3 +36,3 @@ 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1',

function createRequest(method, url, data, options){
const createRequest = (method, url, data, options) => {
return new Promise((resolve, reject) => {

@@ -42,3 +42,3 @@

if(method.toUpperCase() == 'POST') headers['Content-Type'] = 'application/x-www-form-urlencoded'
if(url.indexOf('music.163.com') != -1) headers['Referer'] = 'http://music.163.com'
if(url.includes('music.163.com')) headers['Referer'] = 'http://music.163.com'
// headers['X-Real-IP'] = '118.88.88.88'

@@ -52,6 +52,12 @@

if(options.crypto == 'weapi'){
const csrfToken = (headers['Cookie'] || '').match(/_csrf=([^(;|$)]+)/)
let csrfToken = (headers['Cookie'] || '').match(/_csrf=([^(;|$)]+)/)
data.csrf_token = (csrfToken ? csrfToken[1] : '')
data = encrypt(data)
data = encrypt.weapi(data)
url = url.replace(/\w*api/,'weapi')
}
else if(options.crypto == 'linuxapi'){
data = encrypt.linuxapi({'method': method, url: url.replace(/\w*api/,'api'), 'params': data})
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 = 'http://music.163.com/api/linux/forward'
}

@@ -72,5 +78,2 @@ const answer = {status: 500, body: {}, cookie: []}

answer.status = answer.body.code || res.statusCode
if(answer.body.code=='301'){
answer.body.apiMsg='需要登陆'
}
}

@@ -77,0 +80,0 @@ catch(e){

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc