├── README.md ├── index.js ├── package.json ├── router ├── main.js ├── music.js └── welfare.js ├── util ├── index.js └── service.js └── vender └── sign.js /README.md: -------------------------------------------------------------------------------- 1 | 2 | # 运行 3 | 4 | ``` 5 | npm install 6 | 7 | node index.js 8 | ``` 9 | 10 | #### 2023/01/09 11 | 1. 服务器已经到期了,目前项目的测试API是无法使用的。需要测试的话可能得自己起一个服务了。 12 | 13 | #### 2020/6/03 14 | 15 | 1. 接口已恢复。感谢[蒟蒻...](https://blog.csdn.net/weixin_44159306)的帮助, 才能这么快解决这个问题。有兴趣可以查看他的关于sign获取的[文章](https://blog.csdn.net/weixin_44159306/article/details/106292092) 16 | 2. 之前分出了ts的分支,主要开始维护ts分支,master可能会更新的晚一点,如果需要自己启服务的话可以下载ts分支,有问题也可以回复,我可以协助一下。 17 | 18 | #### 2020/5/27 19 | 20 | 1. 发现获取vkey的接口现在获取不到了,应该是官方换了请求方式,目前播放地址受到了影响,无法播放,修复时间待定。 21 | 22 | #### 2020/5/7 23 | 24 | 1. 之前要使用api的话只能通过服务端请求,嗯嗯..想了想,现在支持跨域了。还是仅测试使用,服务器太渣渣。。经不起造。 25 | 26 | #### 2020/2/25 27 | 28 | 1. 最近在做新的项目,叫小鱼干APP,可以在百度,vivo,小米,apple store 中下载,暂时没有时间来搞这个音乐api的东西了,不过服务没有停,如果想用来测试或者玩玩的还是可以用的。 29 | 30 | 31 | #### 2019/12/26 更新 32 | 33 | 1. 获取歌曲播放地址接口 增加 lyric参数代表是否获取歌词。 34 | 2. 增加获取歌词接口 35 | 36 | 37 | #### 2019/12/24 更新 38 | 39 | 1. 所有音乐接口更改,增加一层music/ ,获取地址改为 music/song 40 | 2. 增加福利图片接口 41 | 3. 示例接口更改为https 42 | 43 | > 接口仅供交流学习使用 44 | 45 | [github 项目地址](https://github.com/lunhui1994/node-music-api) 希望大家给个star 以支撑我初期坚持不懈的动力。 46 | 47 | 因为之前使用网络上别人封装的音乐api现在无法使用,或者一些需要付费使用,当然这也无可厚非。 48 | 49 | 但对我而言,只需要简单的一些音乐api做一些东西。 感觉给钱有点亏。 就自己搞一个简单的符合我的需求的吧。 50 | 51 | # 文档 52 | 53 | 54 | 55 | ## 所支持的Api 56 | 57 | 1. 音乐搜索 58 | 2. 音乐top100列表 59 | 3. 音乐播放地址 60 | 4. 福利图片 (新增 2019/12/24). 61 | 5. 音乐歌词 (新增 2019/12/26). 62 | 63 | 很简单的三个基本的功能。 64 | 65 | 所有方法都是GET 66 | 67 | 本项目所支持的Api 68 | 69 | ## 一、音乐 music 70 | 71 | ### 1、音乐列表 72 | 73 | #### list 74 | 75 | `GET` 76 | 77 | | 参数 | 类型 | 描述 | 78 | | --- | --- | --- | 79 | | p | string | 页码| 80 | | n | string | 数目| 81 | | w | string | 关键词 | 82 | 83 | 84 | 返回参数 85 | 86 | | 上级 | 参数 | 类型 | 描述 | 87 | | --- | --- | --- | --- | 88 | | --- | curpage | int | 页码| 89 | | --- | curnum | int | 数目| 90 | | --- | list | array [ object ] | 音乐列表 | 91 | | list > {} | songname | string | 歌曲名 | 92 | | list > {} | songname | string | 歌曲名 | 93 | | list > {} | albumname | string | 专辑名 | 94 | | list > {} | songmid | string | 歌曲id,用来请求播放地址 | 95 | | list > {} | albumimg | string | 封面图 | 96 | | list > {} | singer | object| 歌手信息 | 97 | | list > {} > singer | id | int | 歌手id | 98 | | list > {} > singer| name | string | 歌手名称 | 99 | eg: 100 | 101 | ```json 102 | https://api.zsfmyz.top/music/list?p=1&n=30&w=蔡徐坤 103 | ``` 104 | 105 | 返回参数举例 106 | 107 | ```json 108 | { 109 | "code": "0", 110 | "data": { 111 | "curnum": 30, 112 | "curpage": 1, 113 | "list": [ 114 | { 115 | "songname": "情人", 116 | "singer": { 117 | "id": 1016794, 118 | "mid": "002nXp292LIOGV", 119 | "name": "蔡徐坤", 120 | "name_hilight": "蔡徐坤" 121 | }, 122 | "albumname": "情人", 123 | "songmid": "000aWBBQ2fMyBJ", 124 | "albumimg": "http://imgcache.qq.com/music/photo/album_300/88/300_albumpic_12583588_0.jpg" 125 | }, 126 | { 127 | "songname": "YOUNG", 128 | "singer": { 129 | "id": 1016794, 130 | "mid": "002nXp292LIOGV", 131 | "name": "蔡徐坤", 132 | "name_hilight": "蔡徐坤" 133 | }, 134 | "albumname": "YOUNG", 135 | "songmid": "004BxrBT3coQnC", 136 | "albumimg": "http://imgcache.qq.com/music/photo/album_300/78/300_albumpic_7156278_0.jpg" 137 | }, 138 | { 139 | "songname": "Wait Wait Wait", 140 | "singer": { 141 | "id": 1016794, 142 | "mid": "002nXp292LIOGV", 143 | "name": "蔡徐坤", 144 | "name_hilight": "蔡徐坤" 145 | }, 146 | "albumname": "Wait Wait Wait", 147 | "songmid": "002kRCBE4dfcU2", 148 | "albumimg": "http://imgcache.qq.com/music/photo/album_300/19/300_albumpic_4359819_0.jpg" 149 | }, 150 | // ..... 151 | ] 152 | } 153 | } 154 | ``` 155 | 156 | ### 2、音乐top100列表 157 | 158 | #### top 159 | `GET` 160 | 161 | 参数 162 | 163 | 无 164 | 165 | 166 | 返回参数 167 | 168 | | 上级 | 参数 | 类型 | 描述 | 169 | | --- | --- | --- | --- | 170 | | --- | date | string | 日期| 171 | | --- | curpage | int | 页码| 172 | | --- | curnum | int | 数目| 173 | | --- | list | --- | 音乐列表 | 174 | | --- | list | array [ object ] | 音乐列表 | 175 | | list > {} | songname | string | 歌曲名 | 176 | | list > {} | songname | string | 歌曲名 | 177 | | list > {} | albumname | string | 专辑名 | 178 | | list > {} | songmid | string | 歌曲id,用来请求播放地址 | 179 | | list > {} | albumimg | string | 封面图 | 180 | | list > {} | singer | object| 歌手信息 | 181 | | list > {} > singer | id | int | 歌手id | 182 | | list > {} > singer| name | string | 歌手名称 | 183 | | --- | topinfo | --- | 音乐top100信息 | 184 | 185 | list中歌曲信息比普通列表多了排名: cur_count 186 | 187 | 188 | 189 | eg: 190 | 191 | ```json 192 | https://api.zsfmyz.top/music/top 193 | ``` 194 | 195 | 返回参数举例 196 | 197 | ```json 198 | { 199 | "code": "0", 200 | "data": { 201 | "code": 0, 202 | "date": "2019-12-05", 203 | "curnum": 100, 204 | "curpage": 1, 205 | "list": [ 206 | { 207 | "cur_count": "1", 208 | "songname": "像极了", 209 | "singer": { 210 | "id": 1441799, 211 | "mid": "0023dQD40to8NP", 212 | "name": "永彬Ryan.B" 213 | }, 214 | "albumname": "像极了", 215 | "songmid": "000V8En93R3Dvd", 216 | "albumimg": "http://imgcache.qq.com/music/photo/album_300/36300_albumpic_9218636_0.jpg" 217 | }, 218 | { 219 | "cur_count": "2", 220 | "songname": "拱手相让", 221 | "singer": { 222 | "id": 22529, 223 | "mid": "001z6uGh1j5qBh", 224 | "name": "胜屿" 225 | }, 226 | "albumname": "拱手相让", 227 | "songmid": "002DIlMZ48qB1F", 228 | "albumimg": "http://imgcache.qq.com/music/photo/album_300/66300_albumpic_9414066_0.jpg" 229 | }, 230 | { 231 | "cur_count": "3", 232 | "songname": "余年", 233 | "singer": { 234 | "id": 1060985, 235 | "mid": "0022eAG537I1bg", 236 | "name": "肖战" 237 | }, 238 | "albumname": "余年", 239 | "songmid": "000bFWrY2VrdVp", 240 | "albumimg": "http://imgcache.qq.com/music/photo/album_300/92300_albumpic_9423892_0.jpg" 241 | }, 242 | { 243 | "cur_count": "4", 244 | "songname": "触不可及", 245 | "singer": { 246 | "id": 199509, 247 | "mid": "003fA5G40k6hKc", 248 | "name": "周深" 249 | }, 250 | "albumname": "触不可及", 251 | "songmid": "002EFRnf3ekI9S", 252 | "albumimg": "http://imgcache.qq.com/music/photo/album_300/4300_albumpic_9320604_0.jpg" 253 | }, 254 | { 255 | "cur_count": "5", 256 | "songname": "冷静和热情之间", 257 | "singer": { 258 | "id": 198135, 259 | "mid": "001IoTZp19YMDG", 260 | "name": "易烊千玺" 261 | }, 262 | "albumname": "冷静和热情之间", 263 | "songmid": "0014YYnw3vadJJ", 264 | "albumimg": "http://imgcache.qq.com/music/photo/album_300/59300_albumpic_9415259_0.jpg" 265 | }, 266 | { 267 | "cur_count": "6", 268 | "songname": "美丽谎言", 269 | "singer": { 270 | "id": 71976, 271 | "mid": "001gthIA2JeIV1", 272 | "name": "都智文" 273 | }, 274 | "albumname": "美丽谎言", 275 | "songmid": "003sJCeZ1iK9mZ", 276 | "albumimg": "http://imgcache.qq.com/music/photo/album_300/88300_albumpic_9353488_0.jpg" 277 | }, 278 | { 279 | "cur_count": "7", 280 | "songname": "那男孩还好吗", 281 | "singer": { 282 | "id": 3298773, 283 | "mid": "003yGiqM2qF7Gm", 284 | "name": "Uu" 285 | }, 286 | "albumname": "那男孩还好吗", 287 | "songmid": "002COmzJ0SPZMl", 288 | "albumimg": "http://imgcache.qq.com/music/photo/album_300/36300_albumpic_9132036_0.jpg" 289 | }, 290 | { 291 | "cur_count": "8", 292 | "songname": "星辰大海", 293 | "singer": { 294 | "id": 25724, 295 | "mid": "0044vhyY2lfSB8", 296 | "name": "周冬雨" 297 | }, 298 | "albumname": "星辰大海", 299 | "songmid": "003enTsq4M1J59", 300 | "albumimg": "http://imgcache.qq.com/music/photo/album_300/63300_albumpic_9305663_0.jpg" 301 | }, 302 | { 303 | "cur_count": "9", 304 | "songname": "Lover (Remix)", 305 | "singer": { 306 | "id": 11921, 307 | "mid": "000qrPik2w6lDr", 308 | "name": "Taylor Swift" 309 | }, 310 | "albumname": "Lover (Remix)", 311 | "songmid": "000H6p9p0V4MXi", 312 | "albumimg": "http://imgcache.qq.com/music/photo/album_300/58300_albumpic_9207358_0.jpg" 313 | }, 314 | ], 315 | "topinfo": { 316 | "ListName": "巅峰榜·新歌", 317 | "MacDetailPicUrl": "http://y.gtimg.cn/music/common/upload/iphone_order_channel/20150820172435.jpg", 318 | "MacListPicUrl": "http://y.gtimg.cn/music/common/upload/iphone_order_channel/20150820172427.jpg", 319 | "UpdateType": "1", 320 | "albuminfo": "", 321 | "headPic_v12": "http://y.gtimg.cn/music/common/upload/iphone_order_channel/20150820174934.jpg", 322 | "info": "集结30天内发行的优质歌曲,鼓励原创、着眼未来的乐坛风向标。根据每日综合数据进行排序,体现QQ音乐用户追新潮流,致力于打造最权威最有公信力的专业健康的新歌排行榜。

歌曲数量:100首
综合数据:登录用户在QQ音乐收听/分享/下载数据", 323 | "listennum": 1497166, 324 | "pic": "http://y.gtimg.cn/music/common/upload/iphone_order_channel/20150820172421.jpg", 325 | "picDetail": "http://y.gtimg.cn/music/common/upload/iphone_order_channel/20150820172414.jpg", 326 | "pic_album": "http://imgcache.qq.com/music/photo_new/T002R300x300M000000tSk703NJAVD.jpg", 327 | "pic_h5": "http://y.gtimg.cn/music/common/upload/iphone_order_channel/20150820172242.jpg", 328 | "pic_v11": "http://y.gtimg.cn/music/common/upload/iphone_order_channel/20150820172421.jpg", 329 | "pic_v12": "http://y.gtimg.cn/music/photo_new/T003R300x300M000003zALCN1hkB6y.jpg", 330 | "topID": "27", 331 | "type": "0" 332 | } 333 | } 334 | } 335 | ``` 336 | 337 | ### 3、音乐播放地址 338 | 339 | #### song 340 | `GET` 341 | 342 | 343 | | 参数 | 类型 | 描述 | 344 | | --- | --- | --- | 345 | | songmid | string | 用于获取token | 346 | | guid | string | 用于获取token| 347 | | lyric | string | 默认为0不获取歌词,1获取歌词 | 348 | 349 | 其他参数固定 350 | 351 | 返回参数 352 | 353 | | 参数 | 类型 | 描述 | 354 | | --- | --- | --- | 355 | | musicUrl | string | 音乐播放地址| 356 | 357 | 358 | eg: 359 | 360 | ```json 361 | https://api.zsfmyz.top/music/song?songmid=000aWBBQ2fMyBJ&guid=126548448 362 | ``` 363 | 364 | 返回参数举例 365 | 366 | ```json 367 | { 368 | "code": "0", 369 | "data": { 370 | "musicUrl": "https://ws.stream.qqmusic.qq.com/C400000aWBBQ2fMyBJ.m4a?guid=126548448&vkey=AD7592245F651C2509E1C9B94E3A234B039967023066203214278A71E9DE4E6F3972EA83BB52F80EE74218B39305B6EAC9B0F6A610B0F947&uin=0&fromtag=66", 371 | "lyric": "无" 372 | } 373 | } 374 | 375 | ``` 376 | 377 | ### 4、音乐歌词 378 | 379 | #### lyric 380 | 381 | `GET` 382 | 383 | | 参数 | 类型 | 描述 | 384 | | --- | --- | --- | 385 | | songmid | string | 用于获取歌词 | 386 | 387 | 其他参数固定 388 | 389 | 返回参数 390 | 391 | | 参数 | 类型 | 描述 | 392 | | --- | --- | --- | 393 | | lyric | string | 歌词内容| 394 | 395 | 396 | eg: 397 | 398 | ```json 399 | https://api.zsfmyz.top/music/lyric?songmid=000wocYU11tSzS 400 | ``` 401 | 402 | 返回参数举例 403 | 404 | ```json 405 | { 406 | "code": "0", 407 | "data": { 408 | "lyric": "[ti:差不多姑娘]\n[ar:G.E.M. 邓紫棋]\n[al:差不多姑娘]\n[by:]\n[offset:0]\n[00:00.00]差不多姑娘 - G.E.M. 邓紫棋\n[00:00.17]\n[00:02.67]差不多的姑娘\n[00:06.27]追逐差不多的漂亮\n[00:11.88]她们差不多的愿望\n[00:17.18]牵着她们鼻子方向\n[00:23.05]我回到差不多的家\n[00:24.38]躺在差不多的沙发\n[00:25.68]微博差不多的刷\n[00:26.99]都吃着差不多的瓜\n[00:28.48]那标题差不多的炸\n[00:29.78]...", 409 | } 410 | } 411 | } 412 | 413 | ``` 414 | 415 | ------ 416 | 417 | 418 | ## 福利图片 welfare 419 | 420 | > 根据每日福利社的接口进行了封装,因为他们的https失效了,所以自己反向代理了。 421 | 422 | ## 图片列表 423 | 424 | #### list 425 | 426 | 427 | | 参数 | 类型 | 描述 | 428 | | --- | --- | --- | 429 | | per_page | string | 每页数据量 | 430 | | page | string | 第几页| 431 | 432 | 其他参数固定 433 | 434 | 返回参数 435 | 436 | 如下: 437 | 438 | 439 | eg: 440 | 441 | ``` 442 | https://api.zsfmyz.top/welfare/list?per_page=20&page=2 443 | ``` 444 | 445 | 返回参数举例 446 | 447 | ``` 448 | { 449 | "code": 0, 450 | "data": { 451 | "error": false, 452 | "results": [ 453 | { 454 | "_id": "5b63cd4e9d21225e0d3f58c9", 455 | "createdAt": "2018-08-03T11:34:38.672Z", 456 | "desc": "2018-08-03", //描述 457 | "publishedAt": "2018-08-03T00:00:00.0Z", 458 | "source": "api", 459 | "type": "福利", // 类型 460 | "url": "https://ww1.sinaimg.cn/large/0065oQSqgy1ftwcw4f4a5j30sg10j1g9.jpg", //图片地址 461 | "used": true, 462 | "who": "lijinshan" 463 | }, 464 | { 465 | "_id": "5b6151509d21225206860f08", 466 | "createdAt": "2018-08-01T14:21:04.556Z", 467 | "desc": "2018-08-01", 468 | "publishedAt": "2018-08-01T00:00:00.0Z", 469 | "source": "api", 470 | "type": "福利", 471 | "url": "https://ww1.sinaimg.cn/large/0065oQSqly1ftu6gl83ewj30k80tites.jpg", 472 | "used": true, 473 | "who": "lijinshan" 474 | }, 475 | // ... 476 | ] 477 | } 478 | } 479 | 480 | ``` 481 | 482 | 483 | 484 | 485 | # 说明 486 | 487 | ### 搜索 488 | - https://c.y.qq.com/soso/fcgi-bin/client_search_cp?aggr=1&cr=1&flag_qc=0&p=1&n=30&w=简单爱 489 | 490 | ### 封面 491 | - http://imgcache.qq.com/music/photo/album_300/[albumid%100]/300_albumpic_[albumid]_0.jpg, albumid%100, albumid 492 | - 比如albumid=8217,封面地址就是 493 | - http://imgcache.qq.com/music/photo/album_300/17/300_albumpic_8217_0.jpg。 494 | 495 | ### 歌曲token 496 | - https://c.y.qq.com/base/fcgi-bin/fcg_music_express_mobile3.fcg?format=json205361747&platform=yqq&cid=205361747&songmid=003lghpv0jfFXG&filename=C400003lghpv0jfFXG.m4a&guid=126548448 497 | 498 | 1. songmid可以从歌曲信息中取到,filename根据songmid生成。 499 | 2. 比如,songmid是003lghpv0jfFXG,则filename就是前缀加上C400,后缀加上.m4a,即C400003lghpv0jfFXG.m4a。 500 | 3. 其他字段format、platform、cid、guid可以写死,但都是必须的。 501 | 502 | ### 拼接播放地址 503 | - http://ws.stream.qqmusic.qq.com/C400003lghpv0jfFXG.m4a?fromtag=0&guid=126548448&vkey=D661E5DF19B8FEB2FBFC554276746AC608AE98B0F30595B3B3BAD5C1C89ECCDD7BE599E306F786621856D22D6BD6B96F5DD344CF3814DB71 504 | 505 | [原文依据](https://www.jianshu.com/p/67e4bd47d981) 506 | 507 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const compression = require('compression') 3 | const path = require('path') 4 | const cache = require('apicache').middleware 5 | const app = express() 6 | 7 | // 路由 8 | const mainRouter = require('./router/main.js'); 9 | 10 | let publicPath = path.resolve(__dirname, 'public'); 11 | 12 | app.use(compression()) 13 | 14 | app.use(express.static(publicPath)) 15 | 16 | // cache 17 | app.use(cache('5 minutes')); 18 | 19 | app.use('/', mainRouter); 20 | 21 | app.use(function(req, res) { 22 | res.statusCode = 404; 23 | res.end("404"); 24 | }) 25 | 26 | app.listen(8001, () => { 27 | console.log('listen on 8001'); 28 | }) 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodeservice", 3 | "version": "1.0.0", 4 | "description": "nodeservice", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [ 10 | "node" 11 | ], 12 | "author": "zsf", 13 | "license": "ISC", 14 | "dependencies": { 15 | "apicache": "^1.5.2", 16 | "axios": "^0.19.0", 17 | "compression": "^1.7.4", 18 | "express": "^4.17.1" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /router/main.js: -------------------------------------------------------------------------------- 1 | // 总路由入口文件 2 | 3 | const express = require('express') 4 | const router = express.Router() 5 | 6 | const musicRouter = require('./music.js') // 音乐路由 7 | const welfareRouter = require('./welfare.js') // 福利路由 8 | 9 | router.use('/music', musicRouter); 10 | router.use('/welfare', welfareRouter); 11 | 12 | module.exports = router; -------------------------------------------------------------------------------- /router/music.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 音乐 api 3 | * 1. /list 音乐列表 4 | * 2. /song 音乐地址 5 | * 3. /top 排行榜 6 | * 4. /lyric 歌词 7 | */ 8 | 9 | // URLSearchParams 处理url参数 10 | // var paramsString = "q=URLUtils.searchParams&topic=api" 11 | // var searchParams = new URLSearchParams(paramsString); 12 | 13 | const express = require('express') 14 | const router = express.Router() 15 | const service = require('../util/service.js') 16 | 17 | router.get('/list', (req, res) => { 18 | service.asyncGetMusicList(req.query).then((data)=>{ 19 | res.end(JSON.stringify({ 20 | code: '0', 21 | data: data 22 | })); 23 | }, err => { 24 | res.end(JSON.stringify(err)); 25 | }) 26 | }) 27 | 28 | router.get('/song', (req, res) => { 29 | service.asyncGetMusicToken(req.query).then((data)=>{ 30 | res.setHeader('Cache-Control', 'public, max-age=86400 '); 31 | res.end(JSON.stringify({ 32 | code: '0', 33 | data: data 34 | })); 35 | }, err => { 36 | res.end(JSON.stringify(err)); 37 | }) 38 | }) 39 | 40 | router.get('/top', (req, res) => { 41 | service.asyncGetMusicTop().then((data)=>{ 42 | res.end(JSON.stringify({ 43 | code: '0', 44 | data: data 45 | })); 46 | }, err => { 47 | res.end(JSON.stringify(err)); 48 | }) 49 | }) 50 | 51 | router.get('/lyric', (req, res) => { 52 | service.asyncGetLyric(req.query).then((data)=>{ 53 | res.end(JSON.stringify({ 54 | code: '0', 55 | data: { 56 | lyric: data.lyric 57 | } 58 | })); 59 | }, err => { 60 | res.end(JSON.stringify(err)); 61 | }) 62 | }) 63 | 64 | module.exports = router; -------------------------------------------------------------------------------- /router/welfare.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 福利 api 3 | * 1. /list 图片列表 4 | * 5 | */ 6 | const express = require('express') 7 | const router = express.Router() 8 | const service = require('../util/service.js') 9 | 10 | router.get('/list', (req, res) => { 11 | service.asyncGetWelfareList(req.query).then((data)=>{ 12 | res.end(JSON.stringify({ 13 | code: 0, 14 | data: data 15 | })); 16 | }, err => { 17 | res.end(JSON.stringify(err)); 18 | }) 19 | }) 20 | 21 | module.exports = router; -------------------------------------------------------------------------------- /util/index.js: -------------------------------------------------------------------------------- 1 | // 解析jsonp返回数据 正则匹配 2 | const jsonpRegExp = new RegExp(/(?<=callback\().*(?=\))/); 3 | 4 | const regExp = { 5 | jsonpRegExp: jsonpRegExp 6 | } 7 | 8 | /** 9 | * {name:'jack',age:12,height:160} => 'jack/12/160' 10 | */ 11 | const json2url = (params) => { 12 | let url = ''; 13 | for (let key in params) { 14 | url += '/' + params[key]; 15 | } 16 | return url; 17 | } 18 | 19 | module.exports = { 20 | regExp: regExp, 21 | json2url: json2url 22 | } -------------------------------------------------------------------------------- /util/service.js: -------------------------------------------------------------------------------- 1 | // 接口函数 2 | 3 | const axios = require('axios') 4 | const util = require('./index.js') 5 | const getSign = require('../vender/sign.js') 6 | 7 | const regExp = util.regExp; 8 | const json2url = util.json2url; 9 | 10 | /** 11 | * @TODO 音乐 axios实例 12 | */ 13 | let musicInterface = axios.create({ 14 | baseURL: 'https://c.y.qq.com', 15 | timeout: 2 * 1000 16 | }) 17 | /** 18 | * @TODO 音乐url axios实例 19 | */ 20 | let musicTokenInterface = axios.create({ 21 | baseURL: 'https://u.y.qq.com', 22 | timeout: 2 * 1000 23 | }) 24 | /** 25 | * @TODO 福利 axios实例 26 | */ 27 | let welfareInterface = axios.create({ 28 | baseURL: 'http://gank.io', 29 | timeout: 2 * 1000 30 | }) 31 | 32 | 33 | // apiUrl 实际代理地址 34 | const apiUrl = { 35 | searchUrl: '/soso/fcgi-bin/client_search_cp', 36 | albumimgUrl: 'http://imgcache.qq.com/music/photo/album_300/', 37 | tokenUrl: '/cgi-bin/musics.fcg', 38 | lyricUrl: '/lyric/fcgi-bin/fcg_query_lyric_new.fcg', 39 | top100Url: '/v8/fcg-bin/fcg_v8_toplist_cp.fcg?g_tk=5381&uin=0&format=json&inCharset=utf-8&outCharset=utf-8¬ice=0&platform=h5&needNewCode=1&tpl=3&page=detail&type=top&topid=27&_=1519963122923', 40 | welfareUrl: '/api/data/福利/', 41 | }; 42 | 43 | // 获取音乐列表 44 | function getMusicList(params, callback) { 45 | return new Promise((resolve, reject) => { 46 | musicInterface({ 47 | method: 'get', 48 | url: apiUrl.searchUrl, 49 | params: params 50 | }).then(res => { 51 | res = JSON.parse(res.data.match(regExp.jsonpRegExp)[0]); 52 | let params = { 53 | curnum: res.data.song.curnum, 54 | curpage: res.data.song.curpage, 55 | list: [] 56 | } 57 | for (let item of res.data.song.list) { 58 | params.list.push({ 59 | songname: item.songname, 60 | singer: item.singer[0], 61 | albumname: item.albumname, 62 | songmid: item.songmid, 63 | albumimg: apiUrl.albumimgUrl + (item.albumid % 100) + '/300_albumpic_' + item.albumid + '_0.jpg' 64 | }) 65 | } 66 | resolve(params); 67 | }, err => { 68 | reject(err); 69 | }) 70 | }) 71 | } 72 | 73 | /** 74 | * @TODO: 获取token 拼接播放地址 75 | * @param: format 'json205361747', 76 | * @param: platform 'yqq', 77 | * @param: cid '205361747', 78 | * @param: guid //126548448 5043253136 79 | * @param: songmid 歌曲songmid,需要在搜索歌曲后获取 80 | * @param: filename 文件名 81 | */ 82 | function getMusicToken(params, lyricData) { 83 | let data = { 84 | "req": { 85 | "module": "CDN.SrfCdnDispatchServer", 86 | "method": "GetCdnDispatch", 87 | "param": { 88 | "guid": params.guid, 89 | "calltype": 0, 90 | "userip": "" 91 | } 92 | }, 93 | "req_0": { 94 | "module": "vkey.GetVkeyServer", 95 | "method": "CgiGetVkey", 96 | "param": { 97 | "guid": params.guid, 98 | "songmid": [ 99 | params.songmid 100 | ], 101 | "songtype": [ 102 | 0 103 | ], 104 | "uin": "562032497", 105 | "loginflag": 1, 106 | "platform": "20" 107 | } 108 | }, 109 | "comm": { 110 | "uin": 562032497, 111 | "format": "json", 112 | "ct": 24, 113 | "cv": 0 114 | } 115 | } 116 | let signStr = getSign(data); 117 | let params_data = { 118 | sign: signStr, 119 | data: data 120 | } 121 | return new Promise(resolve => { 122 | musicTokenInterface({ 123 | method: 'get', 124 | url: apiUrl.tokenUrl, 125 | params: params_data 126 | }).then(res => { 127 | let musicUrl = "https://ws.stream.qqmusic.qq.com/" + res.data.req_0.data.midurlinfo[0].purl; 128 | let lyric = (lyricData && lyricData.code == '0') ? lyricData.lyric : '无'; 129 | resolve({ 130 | vkey: res.data.req.data.vkey, 131 | musicUrl: musicUrl, 132 | lyric: lyric 133 | }); 134 | }, err => { 135 | reject(err); 136 | }) 137 | }) 138 | } 139 | 140 | /** 141 | * @TODO: 获取top100 142 | */ 143 | function getMusicTop() { 144 | return new Promise((resolve, reject) => { 145 | musicInterface({ 146 | method: 'get', 147 | url: apiUrl.top100Url 148 | }).then(res => { 149 | res = res.data; 150 | let params = { 151 | code: res.code, 152 | date: res.date, 153 | curnum: res.total_song_num, 154 | curpage: 1, 155 | list: [], 156 | topinfo: res.topinfo 157 | } 158 | 159 | for (let item of res.songlist) { 160 | params.list.push({ 161 | cur_count: item.cur_count, 162 | songname: item.data.songname, 163 | singer: item.data.singer[0], 164 | albumname: item.data.albumname, 165 | songmid: item.data.songmid, 166 | albumimg: apiUrl.albumimgUrl + item.data.albumid % 100 + '/300_albumpic_' + item.data.albumid + '_0.jpg' 167 | }) 168 | } 169 | resolve(params); 170 | }, err => { 171 | reject(err); 172 | }) 173 | }) 174 | } 175 | 176 | /** 177 | * @TODO: 获取歌词 178 | * @param: songmid 歌曲songmid,需要在搜索歌曲后获取 179 | * @param: format 格式,建议加上format=json 180 | * @param: nobase64 默认0, 必须填1格式化返回数据 181 | */ 182 | function getLyric(params) { 183 | params.format = 'json'; 184 | params.nobase64 = 1; 185 | return new Promise((resolve, reject) => { 186 | musicInterface({ 187 | method: 'get', 188 | headers: { 189 | 'Referer': 'https://y.qq.com/portal/player.html' 190 | }, 191 | url: apiUrl.lyricUrl, 192 | params: params 193 | }).then(res => { 194 | resolve(res.data); 195 | }, err => { 196 | reject(err); 197 | }) 198 | }) 199 | } 200 | 201 | // async 202 | /** 203 | * 204 | * @TODO 后续更改async用法方式。 205 | */ 206 | // 搜索列表 207 | async function asyncGetMusicList(params) { 208 | return await getMusicList(params); 209 | } 210 | // 歌曲地址 211 | async function asyncGetMusicToken(params) { 212 | if (params.lyric == '1') { 213 | var lyricData = await getLyric(params); 214 | return await getMusicToken(params, lyricData); 215 | } else { 216 | return await getMusicToken(params); 217 | } 218 | } 219 | // top100 220 | async function asyncGetMusicTop() { 221 | return await getMusicTop(); 222 | } 223 | // 歌词 224 | async function asyncGetLyric(params) { 225 | return await getLyric(params); 226 | } 227 | 228 | // 获取福利图片列表 229 | function getWelfareList(params, callback) { 230 | return new Promise((resolve, reject) => { 231 | welfareInterface({ 232 | method: 'get', 233 | url: encodeURI(apiUrl.welfareUrl + params.per_page + '/' + params.page), 234 | }).then(res => { 235 | resolve(res.data); 236 | }, err => { 237 | reject(err); 238 | }) 239 | }) 240 | } 241 | 242 | async function asyncGetWelfareList(params) { 243 | return await getWelfareList(params); 244 | } 245 | 246 | module.exports = { 247 | getMusicList: getMusicList, 248 | asyncGetMusicList: asyncGetMusicList, 249 | getMusicToken: getMusicToken, 250 | asyncGetMusicToken: asyncGetMusicToken, 251 | getMusicTop: getMusicTop, 252 | asyncGetMusicTop: asyncGetMusicTop, 253 | getLyric: getLyric, 254 | asyncGetLyric: asyncGetLyric, 255 | getWelfareList: getWelfareList, 256 | asyncGetWelfareList: asyncGetWelfareList, 257 | 258 | } 259 | -------------------------------------------------------------------------------- /vender/sign.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | module.exports = function getSign (data) { 3 | let str = 'abcdefghijklmnopqrstuvwxyz0123456789' 4 | let count = Math.floor(Math.random() * 7 + 10) 5 | let sign = 'zza' 6 | for (let i = 0; i < count; i++) { 7 | sign += str[Math.floor(Math.random() * 36)] 8 | } 9 | sign += createSign('CJBPACrRuNy7' + JSON.stringify(data)) 10 | return sign 11 | } 12 | 13 | function createSign (n, t) { 14 | function f (n, t) { 15 | return n << t | n >>> 32 - t 16 | } 17 | 18 | function h (n, t) { 19 | var o, e, u, p, r 20 | return u = 2147483648 & n, 21 | p = 2147483648 & t, 22 | r = (1073741823 & n) + (1073741823 & t), 23 | (o = 1073741824 & n) & (e = 1073741824 & t) ? 2147483648 ^ r ^ u ^ p : o | 24 | e ? 1073741824 & r ? 3221225472 ^ r ^ u ^ p : 1073741824 ^ r ^ u ^ p : r ^ 25 | u ^ p 26 | } 27 | 28 | function o (n, t, o, e, u, p, r) { 29 | var i 30 | return n = h(n, h(h((i = t) & o | ~i & e, u), r)), 31 | h(f(n, p), t) 32 | } 33 | 34 | function e (n, t, o, e, u, p, r) { 35 | var i 36 | return n = h(n, h(h(t & (i = e) | o & ~i, u), r)), 37 | h(f(n, p), t) 38 | } 39 | 40 | function u (n, t, o, e, u, p, r) { 41 | return n = h(n, h(h(t ^ o ^ e, u), r)), 42 | h(f(n, p), t) 43 | } 44 | 45 | function p (n, t, o, e, u, p, r) { 46 | return n = h(n, h(h(o ^ (t | ~e), u), r)), 47 | h(f(n, p), t) 48 | } 49 | 50 | function r (n) { 51 | var t, o = '', 52 | e = '' 53 | for (t = 0; t <= 3; t++) { 54 | o += (e = '0' + 55 | (n >>> 8 * t & 255).toString(16)).substr(e.length - 2, 2) 56 | } 57 | return o 58 | } 59 | 60 | var i, l, c, g, a, s, v, d, y, b 61 | for (t = t || 32, i = function (n) { 62 | for (var t, o = n.length, 63 | e = o + 8, 64 | u = 16 * (1 + (e - e % 64) / 64), p = Array(u - 1), r = 0, i = 0; i < 65 | o;) { 66 | r = i % 4 * 8, 67 | p[t = (i - i % 4) / 4] = p[t] | n.charCodeAt(i) << r, 68 | i++ 69 | } 70 | return r = i % 4 * 8, 71 | p[t = (i - i % 4) / 4] = p[t] | 128 << r, 72 | p[u - 2] = o << 3, 73 | p[u - 1] = o >>> 29, 74 | p 75 | }(n = function (n) { 76 | n = n.replace(/\r\n/g, '\n') 77 | for (var t = '', 78 | o = 0; o < n.length; o++) { 79 | var e = n.charCodeAt(o) 80 | e < 128 ? t += String.fromCharCode(e) : (127 < e && e < 2048 81 | ? t += String.fromCharCode(e >> 6 | 192) 82 | : (t += String.fromCharCode(e >> 12 | 224), t += String.fromCharCode( 83 | e >> 6 & 63 | 128)), t += String.fromCharCode(63 & e | 128)) 84 | } 85 | return t 86 | }(n)), v = 1732584193, d = 4023233417, y = 2562383102, b = 271733878, l = 0; l < 87 | i.length; l += 16) { 88 | v = o(c = v, g = d, a = y, s = b, i[l + 0], 7, 3614090360), 89 | b = o(b, v, d, y, i[l + 1], 12, 3905402710), 90 | y = o(y, b, v, d, i[l + 2], 17, 606105819), 91 | d = o(d, y, b, v, i[l + 3], 22, 3250441966), 92 | v = o(v, d, y, b, i[l + 4], 7, 4118548399), 93 | b = o(b, v, d, y, i[l + 5], 12, 1200080426), 94 | y = o(y, b, v, d, i[l + 6], 17, 2821735955), 95 | d = o(d, y, b, v, i[l + 7], 22, 4249261313), 96 | v = o(v, d, y, b, i[l + 8], 7, 1770035416), 97 | b = o(b, v, d, y, i[l + 9], 12, 2336552879), 98 | y = o(y, b, v, d, i[l + 10], 17, 4294925233), 99 | d = o(d, y, b, v, i[l + 11], 22, 2304563134), 100 | v = o(v, d, y, b, i[l + 12], 7, 1804603682), 101 | b = o(b, v, d, y, i[l + 13], 12, 4254626195), 102 | y = o(y, b, v, d, i[l + 14], 17, 2792965006), 103 | v = e(v, d = o(d, y, b, v, i[l + 15], 22, 1236535329), y, b, i[l + 1], 5, 104 | 4129170786), 105 | b = e(b, v, d, y, i[l + 6], 9, 3225465664), 106 | y = e(y, b, v, d, i[l + 11], 14, 643717713), 107 | d = e(d, y, b, v, i[l + 0], 20, 3921069994), 108 | v = e(v, d, y, b, i[l + 5], 5, 3593408605), 109 | b = e(b, v, d, y, i[l + 10], 9, 38016083), 110 | y = e(y, b, v, d, i[l + 15], 14, 3634488961), 111 | d = e(d, y, b, v, i[l + 4], 20, 3889429448), 112 | v = e(v, d, y, b, i[l + 9], 5, 568446438), 113 | b = e(b, v, d, y, i[l + 14], 9, 3275163606), 114 | y = e(y, b, v, d, i[l + 3], 14, 4107603335), 115 | d = e(d, y, b, v, i[l + 8], 20, 1163531501), 116 | v = e(v, d, y, b, i[l + 13], 5, 2850285829), 117 | b = e(b, v, d, y, i[l + 2], 9, 4243563512), 118 | y = e(y, b, v, d, i[l + 7], 14, 1735328473), 119 | v = u(v, d = e(d, y, b, v, i[l + 12], 20, 2368359562), y, b, i[l + 5], 4, 120 | 4294588738), 121 | b = u(b, v, d, y, i[l + 8], 11, 2272392833), 122 | y = u(y, b, v, d, i[l + 11], 16, 1839030562), 123 | d = u(d, y, b, v, i[l + 14], 23, 4259657740), 124 | v = u(v, d, y, b, i[l + 1], 4, 2763975236), 125 | b = u(b, v, d, y, i[l + 4], 11, 1272893353), 126 | y = u(y, b, v, d, i[l + 7], 16, 4139469664), 127 | d = u(d, y, b, v, i[l + 10], 23, 3200236656), 128 | v = u(v, d, y, b, i[l + 13], 4, 681279174), 129 | b = u(b, v, d, y, i[l + 0], 11, 3936430074), 130 | y = u(y, b, v, d, i[l + 3], 16, 3572445317), 131 | d = u(d, y, b, v, i[l + 6], 23, 76029189), 132 | v = u(v, d, y, b, i[l + 9], 4, 3654602809), 133 | b = u(b, v, d, y, i[l + 12], 11, 3873151461), 134 | y = u(y, b, v, d, i[l + 15], 16, 530742520), 135 | v = p(v, d = u(d, y, b, v, i[l + 2], 23, 3299628645), y, b, i[l + 0], 6, 136 | 4096336452), 137 | b = p(b, v, d, y, i[l + 7], 10, 1126891415), 138 | y = p(y, b, v, d, i[l + 14], 15, 2878612391), 139 | d = p(d, y, b, v, i[l + 5], 21, 4237533241), 140 | v = p(v, d, y, b, i[l + 12], 6, 1700485571), 141 | b = p(b, v, d, y, i[l + 3], 10, 2399980690), 142 | y = p(y, b, v, d, i[l + 10], 15, 4293915773), 143 | d = p(d, y, b, v, i[l + 1], 21, 2240044497), 144 | v = p(v, d, y, b, i[l + 8], 6, 1873313359), 145 | b = p(b, v, d, y, i[l + 15], 10, 4264355552), 146 | y = p(y, b, v, d, i[l + 6], 15, 2734768916), 147 | d = p(d, y, b, v, i[l + 13], 21, 1309151649), 148 | v = p(v, d, y, b, i[l + 4], 6, 4149444226), 149 | b = p(b, v, d, y, i[l + 11], 10, 3174756917), 150 | y = p(y, b, v, d, i[l + 2], 15, 718787259), 151 | d = p(d, y, b, v, i[l + 9], 21, 3951481745), 152 | v = h(v, c), 153 | d = h(d, g), 154 | y = h(y, a), 155 | b = h(b, s) 156 | } 157 | return 32 == t ? r(v) + r(d) + r(y) + r(b) : r(d) + r(y) 158 | } --------------------------------------------------------------------------------