├── 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 | }
--------------------------------------------------------------------------------