diff --git a/.DS_Store b/.DS_Store index 49b2b5a..bad972f 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.babelrc b/.babelrc deleted file mode 100644 index af0f0c3..0000000 --- a/.babelrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "presets": ["es2015"] -} \ No newline at end of file diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 04ba039..0000000 --- a/.editorconfig +++ /dev/null @@ -1,20 +0,0 @@ -# EditorConfig helps developers define and maintain consistent -# coding styles between different editors and IDEs -# editorconfig.org - -root = true - -[*] - -# Change these settings to your own preference -indent_style = space -indent_size = 2 - -# We recommend you to keep these unchanged -end_of_line = lf -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.md] -trim_trailing_whitespace = false diff --git a/.travis.yml b/.travis.yml index 9733ba3..8eaf77a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,4 @@ language: node_js node_js: - - - 4.0 - 6.2 diff --git a/LICENSE b/LICENSE deleted file mode 100644 index facc240..0000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013-2016 Binaryify - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/README.MD b/README.MD index b0072bb..ce4409a 100644 --- a/README.MD +++ b/README.MD @@ -1,5 +1,5 @@ # NeteaseCloudMusicApi -一个调用网易云音乐 API 的 node 模块 +网易云音乐 nodejs 接口
- -## Start +## 版本新特性 +增加使用文档,完成项目重构,增加更完善的单元测试,版本升级到2.0,升级 api 到 v2+,支持登录并获取用户信息和创建的歌单,可通过获取音乐 url 接口获取用户歌单里的的音乐,获取每日推荐歌单和每日推荐音乐 + +## 环境要求 +需要 NodeJS 6.0+ 环境 + +## 安装 ``` shell -npm install NeteaseCloudMusicApi +$ git clone git@github.com:Binaryify/NeteaseCloudMusicApi.git +$ npm install +``` +## 运行 +``` shell +$ node app.js ``` -## Usage -``` javascript -var api = require('NeteaseCloudMusicApi').api -api.search('年度之歌',function(data){ - console.log(data) -}) -``` -or -``` javascript -import {api} from 'NeteaseCloudMusicApi' -api.search('年度之歌',data => { - console.log(data) -}) +## 使用文档 + +[文档地址](https://binaryify.github.io/NeteaseCloudMusicApi) + +## 单元测试 + +``` shell +$ npm test ``` -## API - -### search -``` javascript -api.search(name:String,[callback:function,onlySong:Boolean default:true,limit:Number default:3, offset:Number default:0]) -``` -说明:onlySong默认为true,如果为false,则返回一个对象,包含songs和mvs,songs和mvs均为数组 - -### lrc -``` javascript -api.lrc(id:Number,[callback:function,lv:Number default:-1]) -``` - -### song - -``` javascript -api.song(id:Number,[callback:function]) -``` - -### getArtistAlbums - -``` javascript -api.getArtistAlbums(id:Number,[callback:function,limit:Number default:3, offset:Number default:0]) -``` - -## getAlbums - -``` javascript -api.getAlbums(id:Number,[callback:function]) -``` - -## getPlaylists - -``` javascript -api.Playlists(id:Number,[callback:function]) -``` + + ## License [The MIT License (MIT)](LICENSE) diff --git a/app.js b/app.js new file mode 100644 index 0000000..f6a9979 --- /dev/null +++ b/app.js @@ -0,0 +1,39 @@ +const express = require('express') +const http = require('http') +const app = express() + +//手机登录 +app.use('/login/cellphone', require('./router/loginCellphone')) + +app.use('/login', require('./router/login')) +// 获取每日推荐歌曲 +app.use('/recommend/songs', require('./router/recommendSongs')) +// 获取每日推荐歌单 +app.use('/recommend/resource', require('./router/recommendResource')) + +app.use('/lyric', require('./router/lyric')) + +app.use('/user/playlist', require('./router/userPlaylist')) + +app.use('/playlist/detail', require('./router/playlistDetail')) + +app.use('/playlist/tracks', require('./router/playlistTracks')) +// 获取音乐 url +app.use('/music/url', require('./router/musicUrl')) +// 搜歌 +app.use('/search', require('.//router/search')) + +app.use('/log/web', require('./router/logWeb')) + +process.on('SIGHUP', () => { + console.log('server: bye bye') + process.exit() +}) + +const port = process.env.PORT || 3000 + +app.listen(port, () => { + console.log(`server running @${port}`) +}) + +module.exports = app \ No newline at end of file diff --git a/build/app.js b/build/app.js deleted file mode 100644 index 6b66f80..0000000 --- a/build/app.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.api = undefined; - -var _search = require('./component/search'); - -var _song = require('./component/song'); - -var _lrc = require('./component/lrc'); - -var _getArtistAlbums = require('./component/getArtistAlbums'); - -var _getAlbums = require('./component/getAlbums'); - -var _getPlaylists = require('./component/getPlaylists'); - -var api = { - search: _search.search, - song: _song.song, - lrc: _lrc.lrc, - getArtistAlbums: _getArtistAlbums.getArtistAlbums, - getAlbums: _getAlbums.getAlbums, - getPlaylists: _getPlaylists.getPlaylists -}; -exports.api = api; \ No newline at end of file diff --git a/build/component/getAlbums.js b/build/component/getAlbums.js deleted file mode 100644 index ec2d6b3..0000000 --- a/build/component/getAlbums.js +++ /dev/null @@ -1,32 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.getAlbums = undefined; - -var _request = require('request'); - -var _request2 = _interopRequireDefault(_request); - -var _config = require('../config'); - -var _util = require('../util'); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -var getAlbums = function getAlbums(id, callback) { - var option = (0, _util.deepCopy)(_config.globalOption); - var url = _config.origin + '/api/album/' + id; - var method = 'get'; - Object.assign(option, { url: url, method: method }); - (0, _request2.default)(option, function (err, res, body) { - if (!err && res.statusCode == 200) { - var info = JSON.parse(body); - callback && callback(JSON.stringify(info, '', 2)); - } else { - console.error(err); - } - }); -}; -exports.getAlbums = getAlbums; \ No newline at end of file diff --git a/build/component/getArtistAlbums.js b/build/component/getArtistAlbums.js deleted file mode 100644 index 85b3391..0000000 --- a/build/component/getArtistAlbums.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.getArtistAlbums = undefined; - -var _request = require('request'); - -var _request2 = _interopRequireDefault(_request); - -var _config = require('../config'); - -var _util = require('../util'); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -var getArtistAlbums = function getArtistAlbums(id, callback) { - var limit = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 3; - var offset = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; - - var option = (0, _util.deepCopy)(_config.globalOption); - var url = _config.origin + '/api/artist/albums/' + id + '?offset=' + offset + '&limit=' + limit; - var method = 'GET'; - Object.assign(option, { url: url, method: method }); - (0, _request2.default)(option, function (err, res, body) { - if (!err && res.statusCode == 200) { - var info = JSON.parse(body); - callback && callback(JSON.stringify(info, '', 2)); - } else { - console.error(err); - } - }); -}; -exports.getArtistAlbums = getArtistAlbums; \ No newline at end of file diff --git a/build/component/getPlaylists b/build/component/getPlaylists deleted file mode 100644 index 3be62f2..0000000 --- a/build/component/getPlaylists +++ /dev/null @@ -1,32 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.getAlbums = undefined; - -var _request = require('request'); - -var _request2 = _interopRequireDefault(_request); - -var _config = require('../config'); - -var _util = require('../util'); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -var getPlaylists = function getPlaylists(id, callback) { - var option = (0, _util.deepCopy)(_config.globalOption); - var url = _config.origin + '/api/playlist/detail?id=' + id; - var method = 'get'; - Object.assign(option, { url: url, method: method }); - (0, _request2.default)(option, function (err, res, body) { - if (!err && res.statusCode == 200) { - var info = JSON.parse(body); - callback && callback(JSON.stringify(info, '', 2)); - } else { - console.error(err); - } - }); -}; -exports.getPlaylists = getPlaylists; diff --git a/build/component/getPlaylists.js b/build/component/getPlaylists.js deleted file mode 100644 index e4c8ef5..0000000 --- a/build/component/getPlaylists.js +++ /dev/null @@ -1,32 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.getPlaylists = undefined; - -var _request = require('request'); - -var _request2 = _interopRequireDefault(_request); - -var _config = require('../config'); - -var _util = require('../util'); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -var getPlaylists = function getPlaylists(id, callback) { - var option = (0, _util.deepCopy)(_config.globalOption); - var url = _config.origin + '/api/playlist/detail?id=' + id; - var method = 'get'; - Object.assign(option, { url: url, method: method }); - (0, _request2.default)(option, function (err, res, body) { - if (!err && res.statusCode == 200) { - var info = JSON.parse(body); - callback && callback(JSON.stringify(info, '', 2)); - } else { - console.error(err); - } - }); -}; -exports.getPlaylists = getPlaylists; \ No newline at end of file diff --git a/build/component/lrc.js b/build/component/lrc.js deleted file mode 100644 index cc79f5e..0000000 --- a/build/component/lrc.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.lrc = undefined; - -var _request = require('request'); - -var _request2 = _interopRequireDefault(_request); - -var _config = require('../config'); - -var _util = require('../util'); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -var lrc = function lrc(id) { - var callback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; - var lv = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : -1; - - var option = (0, _util.deepCopy)(_config.globalOption); - var url = _config.origin + '/api/song/lyric?lv=' + lv + '&id=' + id; - var method = 'GET'; - Object.assign(option, { url: url, method: method }); - (0, _request2.default)(option, function (err, res, body) { - if (!err && res.statusCode == 200) { - var info = JSON.parse(body); - callback && callback(JSON.stringify(info, '', 2)); - } else { - console.error(err); - } - }); -}; -exports.lrc = lrc; \ No newline at end of file diff --git a/build/component/search.js b/build/component/search.js deleted file mode 100644 index 50ff324..0000000 --- a/build/component/search.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.search = undefined; - -var _request = require('request'); - -var _request2 = _interopRequireDefault(_request); - -var _config = require('../config'); - -var _util = require('../util'); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -var search = function search() { - var name = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null; - var callback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; - var onlySong = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; - var limit = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 3; - var offset = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0; - - var option = (0, _util.deepCopy)(_config.globalOption); - var url = _config.origin + '/api/search/get'; - var form = { - s: name, - limit: limit, - type: 1, - offset: offset - }; - var method = 'POST'; - Object.assign(option, { url: url, form: form, method: method }); - (0, _request2.default)(option, function (err, res, body) { - if (!err && res.statusCode == 200) { - var info = JSON.parse(body); - var data = void 0; - if (onlySong) { - data = info.result.songs; - } else { - data = { songs: info.result.songs, mvs: info.result.mvs }; - } - callback && callback(JSON.stringify(data, '', 2)); - } else { - console.error(err); - } - }); -}; - -exports.search = search; \ No newline at end of file diff --git a/build/component/song.js b/build/component/song.js deleted file mode 100644 index d88a6ee..0000000 --- a/build/component/song.js +++ /dev/null @@ -1,34 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -exports.song = undefined; - -var _request = require('request'); - -var _request2 = _interopRequireDefault(_request); - -var _config = require('../config'); - -var _util = require('../util'); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -var song = function song(id) { - var callback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; - - var option = (0, _util.deepCopy)(_config.globalOption); - var url = _config.origin + '/api/song/detail?ids=%5B' + id + '%5d'; - var method = 'GET'; - Object.assign(option, { url: url, method: method }); - (0, _request2.default)(option, function (err, res, body) { - if (!err && res.statusCode == 200) { - var info = JSON.parse(body); - callback && callback(JSON.stringify(info, '', 2)); - } else { - console.error(err); - } - }); -}; -exports.song = song; \ No newline at end of file diff --git a/build/config.js b/build/config.js deleted file mode 100644 index 7a686ef..0000000 --- a/build/config.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict'; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -var origin = 'http://music.163.com'; - -var globalOption = { - headers: { - 'Origin': origin, - 'Referer': origin, - 'Content-Type': 'application/x-www-form-urlencoded' - }, - proxy: false -}; - -exports.origin = origin; -exports.globalOption = globalOption; \ No newline at end of file diff --git a/build/util.js b/build/util.js deleted file mode 100644 index 6800069..0000000 --- a/build/util.js +++ /dev/null @@ -1,10 +0,0 @@ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -var deepCopy = function deepCopy(obj) { - return JSON.parse(JSON.stringify(obj)); -}; - -exports.deepCopy = deepCopy; \ No newline at end of file diff --git a/docs/README.md b/docs/README.md index 99da9c2..f7e449a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,17 +7,18 @@ ## 安装 ``` shell -npm install NeteaseCloudMusicApi +$ git clone git@github.com:Binaryify/NeteaseCloudMusicApi.git +$ npm install ``` -## 使用 +## 运行 ``` shell -node app.js +$ node app.js ``` 服务器启动,默认端口为3000 -## 接口 +## 接口文档 ### 登录 登录有两个接口 @@ -89,6 +90,7 @@ node app.js `/recommend/resource` 返回数据如下图:  + ### 获取每日推荐歌曲 说明:调用此接口,可获得每日推荐歌曲(需要登录) 接口地址: diff --git a/package.json b/package.json index aad3968..3dc99af 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,21 @@ { "name": "NeteaseCloudMusicApi", - "version": "1.5.2", - "description": "网易云音乐nodejs版接口模块", - "main": "build/app.js", + "version": "2.0.0", + "description": "", "scripts": { - "test": "node test/test.js", - "build": "babel src/ -d build/" + "start": "node app.js", + "test": "mocha -r intelli-espower-loader -t 20000 test" }, - "keywords": ["NeteaseCloudMusic","网易云音乐","网易云"], - "author": "traveller", - "license": "MIT", + "keywords": [], + "author": "", + "license": "ISC", "dependencies": { - "request": "^2.72.0" + "big-integer": "^1.6.17", + "express": "^4.15.2" }, "devDependencies": { - "babel-core": "^6.9.1", - "babel-preset-es2015": "^6.9.0" + "intelli-espower-loader": "^1.0.1", + "mocha": "^3.2.0", + "power-assert": "^1.4.2" } -} +} \ No newline at end of file diff --git a/router/logWeb.js b/router/logWeb.js new file mode 100644 index 0000000..04388dd --- /dev/null +++ b/router/logWeb.js @@ -0,0 +1,26 @@ +const express = require("express") +const router = express() +const { createWebAPIRequest } = require("../util/util") + +router.get("/", (req, res) => { + const cookie = req.get('Cookie') ? req.get('Cookie') : '' + const data = { + "action": req.query.action, + "json": req.query.json, + "csrf_token": "", + } + + createWebAPIRequest( + 'music.163.com', + '/weapi/log/web', + 'POST', + data, + cookie, + music_req => res.send(music_req), + err => res.status(502).send('fetch error') + ) +}) + + + +module.exports = router \ No newline at end of file diff --git a/router/login.js b/router/login.js new file mode 100644 index 0000000..ae76cd3 --- /dev/null +++ b/router/login.js @@ -0,0 +1,34 @@ +const express = require("express") +const crypto = require('crypto') +const router = express() +const { createWebAPIRequest } = require("../util/util") + +router.get("/", (req, res) => { + const email = req.query.email + const cookie = req.get('Cookie') ? req.get('Cookie') : '' + const md5sum = crypto.createHash('md5') + md5sum.update(req.query.password) + const data = { + 'username': email, + 'password': md5sum.digest('hex'), + 'rememberLogin': 'true' + } + + createWebAPIRequest( + 'music.163.com', + '/weapi/login', + 'POST', + data, + cookie, + (music_req, cookie) => { + console.log(music_req) + res.set({ + 'Set-Cookie': cookie, + }) + res.send(music_req) + }, + err => res.status(502).send('fetch error') + ) +}) + +module.exports = router \ No newline at end of file diff --git a/router/loginCellphone.js b/router/loginCellphone.js new file mode 100644 index 0000000..c908c61 --- /dev/null +++ b/router/loginCellphone.js @@ -0,0 +1,34 @@ +const express = require("express") +const crypto = require('crypto') +const router = express() +const { createWebAPIRequest } = require("../util/util") + +router.get("/", (req, res) => { + const phone = req.query.phone + const cookie = req.get('Cookie') ? req.get('Cookie') : '' + const md5sum = crypto.createHash('md5') + md5sum.update(req.query.password) + const data = { + 'phone': phone, + 'password': md5sum.digest('hex'), + 'rememberLogin': 'true' + } + + createWebAPIRequest( + 'music.163.com', + '/weapi/login/cellphone', + 'POST', + data, + cookie, + (music_req, cookie) => { + console.log(music_req) + res.set({ + 'Set-Cookie': cookie, + }) + res.send(music_req) + }, + err => res.status(502).send('fetch error') + ) +}) + +module.exports = router \ No newline at end of file diff --git a/router/lyric.js b/router/lyric.js new file mode 100644 index 0000000..fec3915 --- /dev/null +++ b/router/lyric.js @@ -0,0 +1,18 @@ +const express = require("express") +const router = express() +const { createRequest } = require("../util/util") + +router.get("/", (req, res) => { + const id = req.query.id + createRequest('/api/song/lyric?os=osx&id=' + id + '&lv=-1&kv=-1&tv=-1', 'GET', null) + .then(result => { + res.setHeader("Content-Type", "application/json") + res.send(result) + }) + .catch(err => { + res.status(502).send('fetch error') + }) +}) + + +module.exports = router \ No newline at end of file diff --git a/router/musicUrl.js b/router/musicUrl.js new file mode 100644 index 0000000..e3b72a6 --- /dev/null +++ b/router/musicUrl.js @@ -0,0 +1,31 @@ +const express = require("express") +const router = express() +const { createWebAPIRequest } = require("../util/util") + +router.get("/", (req, res) => { + const id = parseInt(req.query.id) + const br = parseInt(req.query.br || 999000) + const data = { + "ids": [id], + "br": br, + "csrf_token": "" + } + const cookie = req.get('Cookie') ? req.get('Cookie') : '' + + createWebAPIRequest( + 'music.163.com', + '/weapi/song/enhance/player/url', + 'POST', + data, + cookie, + music_req => { + res.setHeader("Content-Type", "application/json") + res.send(music_req) + }, + err => { + res.status(502).send('fetch error') + } + ) +}) + +module.exports = router \ No newline at end of file diff --git a/router/playlistDetail.js b/router/playlistDetail.js new file mode 100644 index 0000000..1a653ba --- /dev/null +++ b/router/playlistDetail.js @@ -0,0 +1,68 @@ +const http = require('http') +const express = require("express") +const router = express() +const { createWebAPIRequest } = require("../util/util") + +router.get("/", (req, res) => { + const cookie = req.get('Cookie') ? req.get('Cookie') : '' + let detail, imgurl + const data = { + "id": req.query.id, + "offset": 0, + "total": true, + "limit": 1000, + "n": 1000, + "csrf_token": "" + } + + createWebAPIRequest( + 'music.163.com', + '/weapi/v3/playlist/detail', + 'POST', + data, + cookie, + music_req=> { + console.log(music_req) + detail = music_req + mergeRes() + }, + err =>{ + res.status(502).send('fetch error') + } + ) + + // FIXME:i dont know the api to get coverimgurl + // so i get it by parsing html + const http_client = http.get({ + hostname: 'music.163.com', + path: '/playlist?id=' + req.query.id, + headers: { + 'Referer': 'http://music.163.com', + }, + }, function (res) { + res.setEncoding('utf8') + let html = '' + res.on('data', function (chunk) { + html += chunk + }) + res.on('end', function () { + console.log('end', html) + const regImgCover = /\