├── .autod.conf.js
├── .eslintignore
├── .eslintrc
├── .github
└── workflows
│ └── nodejs.yml
├── .gitignore
├── .travis.yml
├── .vscode
└── settings.json
├── LICENSE
├── README.md
├── app
├── controller
│ ├── BaseController.ts
│ ├── albumInfo.ts
│ ├── banner.ts
│ ├── comment.ts
│ ├── getTagList.ts
│ ├── lrc.ts
│ ├── musicInfo.ts
│ ├── musicList.ts
│ ├── mv.ts
│ ├── playList.ts
│ ├── playUrl.ts
│ ├── radio.ts
│ ├── rank.ts
│ ├── recGedan.ts
│ ├── recSinger.ts
│ ├── search.ts
│ └── singer.ts
├── public
│ └── index.html
├── router.ts
├── schedule
│ └── logs.ts
├── service
│ ├── BaseService.ts
│ ├── albumInfo.ts
│ ├── banner.ts
│ ├── comment.ts
│ ├── getTagList.ts
│ ├── lrc.ts
│ ├── musicInfo.ts
│ ├── musicList.ts
│ ├── mv.ts
│ ├── playList.ts
│ ├── playUrl.ts
│ ├── radio.ts
│ ├── rank.ts
│ ├── recGedan.ts
│ ├── recSinger.ts
│ ├── search.ts
│ └── singer.ts
└── utils
│ └── secret.js
├── appveyor.yml
├── config
├── config.default.ts
├── config.local.ts
├── config.prod.ts
└── plugin.ts
├── docs
├── .nojekyll
├── README.md
├── _coverpage.md
├── favicon.ico
└── index.html
├── package-lock.json
├── package.json
├── tsconfig.json
└── typings
├── app
├── controller
│ └── index.d.ts
├── index.d.ts
└── service
│ └── index.d.ts
├── config
├── index.d.ts
└── plugin.d.ts
└── index.d.ts
/.autod.conf.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | write: true,
5 | plugin: 'autod-egg',
6 | prefix: '^',
7 | devprefix: '^',
8 | exclude: [
9 | 'test/fixtures',
10 | 'coverage',
11 | ],
12 | dep: [
13 | 'egg',
14 | 'egg-scripts',
15 | ],
16 | devdep: [
17 | 'autod',
18 | 'autod-egg',
19 | 'egg-bin',
20 | 'tslib',
21 | 'typescript',
22 | ],
23 | keep: [
24 | ],
25 | semver: [
26 | ],
27 | test: 'scripts',
28 | };
29 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/*.d.ts
2 | node_modules/
3 | test/
4 | typings/
5 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["eslint-config-egg/typescript","eslint-config-standard","eslint:recommended","plugin:jsdoc/recommended"],
3 | "plugins": ["jsdoc"],
4 | "rules": {
5 | "comma-dangle":["error","always-multiline"],
6 | "operator-linebreak":["error","before"],
7 | "space-before-function-paren":[
8 | "error",
9 | {
10 | "anonymous":"always",
11 | "named":"always",
12 | "asyncArrow":"always"
13 | }
14 | ],
15 | "linebreak-style":0,
16 | "no-var-requires":0,
17 | "no-return-assign":"off",
18 | "default-case":"off",
19 | "no-useless-constructor":"off",
20 | "no-unused-vars":0,
21 | "jsdoc/require-param-description":"off",
22 | "jsdoc/check-tag-names": 0,
23 | "jsdoc/no-undefined-types":0,
24 | "jsdoc/valid-types":0,
25 | "jsdoc/tag-lines":0,
26 | "jsdoc/require-returns":0,
27 | "jsdoc/check-param-names":0
28 | },
29 | "overrides":[
30 | {
31 | "files":["*.ts"],
32 | "rules":{
33 | "@typescript-eslint/no-unused-vars":0,
34 | "@typescript-eslint/semi":0,
35 | "@typescript-eslint/no-var-requires":0,
36 | "@typescript-eslint/no-useless-constructor":0
37 | }
38 | }
39 | ],
40 | "globals": {
41 | "_":"readonly",
42 | "app":true
43 | },
44 | "env": {
45 | "node": true
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/.github/workflows/nodejs.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Node.js CI
5 |
6 | on:
7 | push:
8 | branches: [ master ]
9 | pull_request:
10 | branches: [ master ]
11 | schedule:
12 | - cron: '0 2 * * *'
13 |
14 | jobs:
15 | build:
16 | runs-on: ${{ matrix.os }}
17 |
18 | strategy:
19 | fail-fast: false
20 | matrix:
21 | node-version: [8]
22 | os: [ubuntu-latest, windows-latest, macos-latest]
23 |
24 | steps:
25 | - name: Checkout Git Source
26 | uses: actions/checkout@v2
27 |
28 | - name: Use Node.js ${{ matrix.node-version }}
29 | uses: actions/setup-node@v1
30 | with:
31 | node-version: ${{ matrix.node-version }}
32 |
33 | - name: Install Dependencies
34 | run: npm i -g npminstall && npminstall
35 |
36 | - name: Continuous Integration
37 | run: npm run ci
38 |
39 | - name: Code Coverage
40 | uses: codecov/codecov-action@v1
41 | with:
42 | token: ${{ secrets.CODECOV_TOKEN }}
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | logs/
2 | npm-debug.log
3 | node_modules/
4 | coverage/
5 | .idea/
6 | run/
7 | logs/
8 | .DS_Store
9 | *.swp
10 | *.lock
11 | *.js
12 | !.autod.conf.js
13 |
14 | app/**/*.js
15 | test/**/*.js
16 | config/**/*.js
17 | app/**/*.map
18 | test/**/*.map
19 | config/**/*.map
20 | app/**/*.d.ts
21 | config/**/*.d.ts
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 |
2 | language: node_js
3 | node_js:
4 | - '8'
5 | before_install:
6 | - npm i npminstall -g
7 | install:
8 | - npminstall
9 | script:
10 | - npm run ci
11 | after_script:
12 | - npminstall codecov && codecov
13 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.codeActionsOnSave": {
3 | "source.fixAll": true
4 | },
5 | "eslint.validate": ["javascript", "javascriptreact","typescript","typescriptreact"],
6 |
7 | "files.exclude": {
8 | "**/.git": true,
9 | "**/.svn": true,
10 | "**/.hg": true,
11 | "**/CVS": true,
12 | "**/.DS_Store": true,
13 | "**/node_modules":true,
14 | "**/.github":true
15 | },
16 | "files.watcherExclude": {
17 | "**/.git": true,
18 | "**/.svn": true,
19 | "**/.hg": true,
20 | "**/CVS": true,
21 | "**/.DS_Store": true,
22 | "**/node_modules":true,
23 | "**/.github":true
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [2021] [name of copyright qyhqiu]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 酷我音樂API
2 |
3 | ### Development
4 |
5 | ## 安装
6 |
7 | ```bash
8 | $ git clone https://github.com/QiuYaohong/kuwoMusicApi.git
9 | $ npm i
10 | $ npm run dev
11 | ```
12 |
13 | ## 环境要求
14 |
15 | - Node.js 8.x
16 | - Typescript 2.8+
17 |
18 | ## 使用文档
19 |
20 | - 项目启动后 默认本地服务为 http://127.0.0.1:7002
21 | - 接口完整地址 为 本地服务地址 + 接口地址
22 | 
23 | - 例如歌曲播放链接: http://127.0.0.1:7002/kuwo/url?mid=162457325&type=music
24 |
25 | [接口文档地址](https://qiuyaohong.github.io/kuwoMusicApi/)
26 |
27 | ## 部署
28 |
29 | ```shell
30 |
31 | 1. npm run start # 启动服务、可用于部署服务器
32 |
33 | 2. npm run stop # 停止服务
34 |
35 | 3. npm run dev # 本地运行服务
36 |
37 | ```
--------------------------------------------------------------------------------
/app/controller/BaseController.ts:
--------------------------------------------------------------------------------
1 | import { Controller } from 'egg'
2 | class BaseController extends Controller {
3 | constructor (ctx) {
4 | super(ctx)
5 | }
6 | }
7 |
8 | module.exports = BaseController
9 |
--------------------------------------------------------------------------------
/app/controller/albumInfo.ts:
--------------------------------------------------------------------------------
1 | const BaseController = require('./BaseController')
2 |
3 | export default class AlbumInfo extends BaseController {
4 | async index () {
5 | const { ctx, service } = this
6 | const { albumId, pn = 1, rn = 30 } = ctx.query
7 |
8 | if (!albumId) {
9 | ctx.body = {
10 | code: 500,
11 | message: '参数错误',
12 | result: null,
13 | success: false,
14 | }
15 | return false
16 | }
17 | const res = await service.albumInfo.getList({ albumId, pn, rn })
18 | ctx.body = res
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/controller/banner.ts:
--------------------------------------------------------------------------------
1 | const BaseController = require('./BaseController')
2 |
3 | export default class Banner extends BaseController {
4 | async index () {
5 | const { ctx, service } = this
6 | const res = await service.banner.List()
7 | ctx.body = res
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/app/controller/comment.ts:
--------------------------------------------------------------------------------
1 | const BaseController = require('./BaseController')
2 |
3 | export default class Momment extends BaseController {
4 | /**
5 | * @param {string} type 评论类型 [热门评论 get_rec_comment , 最新评论 get_comment]
6 | * @param {number} digest 15 歌曲 2 排行榜 8 歌单评论 7 mv评论
7 | */
8 | async index () {
9 | const { ctx, service } = this
10 |
11 | const { sid, page = 1, rows = 30, uid = 0, type = 'get_rec_comment', digest = 15 } = ctx.query
12 |
13 | if (!sid) {
14 | ctx.body = {
15 | code: 500,
16 | message: '参数错误',
17 | result: null,
18 | success: false,
19 | }
20 | return false
21 | }
22 |
23 | const res = await service.comment.List({ sid, page, rows, uid, type, digest })
24 | ctx.body = res
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/controller/getTagList.ts:
--------------------------------------------------------------------------------
1 | const BaseController = require('./BaseController')
2 |
3 | export default class TagList extends BaseController {
4 | async index () {
5 | const { ctx, service } = this
6 |
7 | const res = await service.getTagList.index()
8 |
9 | ctx.body = res
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/app/controller/lrc.ts:
--------------------------------------------------------------------------------
1 | const BaseController = require('./BaseController')
2 |
3 | export default class Lrc extends BaseController {
4 | async index () {
5 | const { ctx, service } = this
6 | const { musicId } = ctx.query
7 |
8 | if (!musicId) {
9 | ctx.body = {
10 | code: 500,
11 | message: '参数错误',
12 | result: null,
13 | success: false,
14 | }
15 | return false
16 | }
17 |
18 | const res = await service.lrc.LrcRes(musicId)
19 |
20 | ctx.body = res
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/controller/musicInfo.ts:
--------------------------------------------------------------------------------
1 | const BaseController = require('./BaseController')
2 |
3 | export default class MusicInfo extends BaseController {
4 | async index () {
5 | const { ctx, service } = this
6 | const { mid } = ctx.query
7 |
8 | if (!mid) {
9 | ctx.body = {
10 | code: 500,
11 | message: '参数错误',
12 | result: null,
13 | success: false,
14 | }
15 | return false
16 | }
17 |
18 | const res = await service.musicInfo.getList(mid)
19 |
20 | ctx.body = res
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/controller/musicList.ts:
--------------------------------------------------------------------------------
1 | const BaseController = require('./BaseController')
2 |
3 | export default class MusicList extends BaseController {
4 | async index () {
5 | const { ctx, service } = this
6 | const { pid, rn = 30, pn = 1 } = ctx.query
7 | if (!pid) {
8 | ctx.body = {
9 | code: 500,
10 | message: '参数错误',
11 | result: null,
12 | success: false,
13 | }
14 | return false
15 | }
16 | const res = await service.musicList.getList({ pid, rn, pn })
17 |
18 | ctx.body = res
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app/controller/mv.ts:
--------------------------------------------------------------------------------
1 | const BaseController = require('./BaseController')
2 |
3 | export default class Mv extends BaseController {
4 | async getMvUrl () {
5 | const { ctx, service } = this
6 |
7 | const { rid } = ctx.query
8 |
9 | if (!rid) {
10 | ctx.body = {
11 | code: 500,
12 | message: '参数错误',
13 | result: null,
14 | success: false,
15 | }
16 | return
17 | }
18 | const res = await service.mv.getMvUrl(rid)
19 | ctx.body = res
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/controller/playList.ts:
--------------------------------------------------------------------------------
1 | const BaseController = require('./BaseController')
2 |
3 | export default class PlayList extends BaseController {
4 | async index () {
5 | const { ctx, service } = this
6 | // 默认 new = 最新 hot = 最热
7 | const { order = 'new', rn = 30, pn = 1 } = ctx.query
8 |
9 | const res = await service.playList.index({ order, rn, pn })
10 | ctx.body = res
11 | }
12 |
13 | // 歌单分类
14 | async getTagPlayList () {
15 | const { ctx, service } = this
16 | const { id, rn = 30, pn = 1 } = ctx.query
17 | if (!id) {
18 | ctx.body = {
19 | code: 500,
20 | message: '参数错误',
21 | result: null,
22 | success: false,
23 | }
24 | return false
25 | }
26 | const res = await service.playList.getTagPlayList({ id, rn, pn })
27 |
28 | ctx.body = res
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/controller/playUrl.ts:
--------------------------------------------------------------------------------
1 | const BaseController = require('./BaseController')
2 |
3 | export default class PlayUrl extends BaseController {
4 | async index () {
5 | const { ctx, service } = this
6 | const { mid, type, br } = ctx.query
7 |
8 | if (!mid) {
9 | ctx.body = {
10 | code: 500,
11 | message: '参数错误',
12 | result: null,
13 | success: false,
14 | }
15 | return false
16 | }
17 |
18 | const res = await service.playUrl.getPlayUrl(mid, type, br)
19 |
20 | ctx.body = res
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/controller/radio.ts:
--------------------------------------------------------------------------------
1 | const BaseController = require('./BaseController')
2 |
3 | export default class Radio extends BaseController {
4 | async index () {
5 | const { ctx, service } = this
6 |
7 | const res = await service.radio.getRadio()
8 |
9 | ctx.body = res
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/app/controller/rank.ts:
--------------------------------------------------------------------------------
1 | const BaseController = require('./BaseController')
2 |
3 | export default class Rank extends BaseController {
4 | async index () {
5 | const { ctx, service } = this
6 | const res = await service.rank.index()
7 | ctx.body = res
8 | }
9 |
10 | // 排行榜音乐
11 | async rankMusicList () {
12 | const { ctx, service } = this
13 | const { bangId = 93, pn = 1, rn = 30 } = ctx.query
14 | const res = await service.rank.getRankMusicList({ bangId, pn, rn })
15 | ctx.body = res
16 | }
17 |
18 | // 推荐榜单
19 |
20 | async rankRecBangList () {
21 | const { ctx, service } = this
22 | const res = await service.rank.getRecBangList()
23 | ctx.body = res
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/controller/recGedan.ts:
--------------------------------------------------------------------------------
1 | const BaseController = require('./BaseController')
2 |
3 | export default class RecGedan extends BaseController {
4 | async index () {
5 | const { ctx, service } = this
6 | const { id = 'rec', pn = 5, rn = 1 } = ctx.query
7 | const res = await service.recGedan.index({ id, pn, rn })
8 | ctx.body = res
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/app/controller/recSinger.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable jsdoc/check-param-names */
2 | const BaseController = require('./BaseController')
3 |
4 | export default class RecSinger extends BaseController {
5 | /**
6 | * @param {number} category // 11 華語 13 歐美 12 日韓 16 組合
7 | * @param {number} pn 分页
8 | * @param {number} rn 每页数据
9 | */
10 | async index () {
11 | const { ctx, service } = this
12 | const { category = 1, pn = 5, rn = 1 } = ctx.query
13 | const res = await service.recSinger.index({ category, pn, rn })
14 | ctx.body = res
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/controller/search.ts:
--------------------------------------------------------------------------------
1 | const BaseController = require('./BaseController')
2 |
3 | export default class Search extends BaseController {
4 | // 关键字搜索
5 | async searchKey () {
6 | const { ctx, service } = this
7 | const { key } = ctx.query
8 | const res = await service.search.searchKey(key)
9 | ctx.body = res
10 | }
11 |
12 | // 单曲搜索
13 | async searchMusicBykeyWord () {
14 | const { ctx, service } = this
15 | const { key, pn = 1, rn = 30 } = ctx.query
16 | if (!key) {
17 | ctx.body = {
18 | code: 500,
19 | message: '参数错误',
20 | result: null,
21 | success: false,
22 | }
23 | return
24 | }
25 | const res = await service.search.searchMusicBykeyWord({ key, pn, rn })
26 | ctx.body = res
27 | }
28 |
29 | // 专辑搜索
30 | async searchAlbumBykeyWord () {
31 | const { ctx, service } = this
32 | const { key, pn = 1, rn = 30 } = ctx.query
33 | if (!key) {
34 | ctx.body = {
35 | code: 500,
36 | message: '参数错误',
37 | result: null,
38 | success: false,
39 | }
40 | return
41 | }
42 | const res = await service.search.searchAlbumBykeyWord({ key, pn, rn })
43 | ctx.body = res
44 | }
45 |
46 | // mv 搜索
47 | async searchMvBykeyWord () {
48 | const { ctx, service } = this
49 | const { key, pn = 1, rn = 30 } = ctx.query
50 | if (!key) {
51 | ctx.body = {
52 | code: 500,
53 | message: '参数错误',
54 | result: null,
55 | success: false,
56 | }
57 | return
58 | }
59 | const res = await service.search.searchMvBykeyWord({ key, pn, rn })
60 | ctx.body = res
61 | }
62 |
63 | // 歌单搜索
64 | async searchPlayListBykeyWord () {
65 | const { ctx, service } = this
66 | const { key, pn = 1, rn = 30 } = ctx.query
67 | if (!key) {
68 | ctx.body = {
69 | code: 500,
70 | message: '参数错误',
71 | result: null,
72 | success: false,
73 | }
74 | return
75 | }
76 | const res = await service.search.searchPlayListBykeyWord({ key, pn, rn })
77 | ctx.body = res
78 | }
79 |
80 | // 歌手搜索
81 | async searchArtistBykeyWord () {
82 | const { ctx, service } = this
83 | const { key, pn = 1, rn = 30 } = ctx.query
84 | if (!key) {
85 | ctx.body = {
86 | code: 500,
87 | message: '参数错误',
88 | result: null,
89 | success: false,
90 | }
91 | return
92 | }
93 | const res = await service.search.searchArtistBykeyWord({ key, pn, rn })
94 | ctx.body = res
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/app/controller/singer.ts:
--------------------------------------------------------------------------------
1 | const BaseController = require('./BaseController')
2 |
3 | export default class Singer extends BaseController {
4 | /**
5 | * @param {number} category 分类 0 = 全部 1 = 华语男 2 = 华语女 3 = 华语组合 4 = 日韩男 5 = 日韩女 6 = 日韩组合 7 = 欧美男 8 = 欧美女 9 = 欧美组合 10 = 其他
6 | * @param {number} rn 每页数据
7 | * @param {number} pn 分页
8 | * @param {string} prefix A~Z 分类
9 | */
10 | async getArtistInfo () {
11 | const { ctx, service } = this
12 | const {
13 | category = 0,
14 | rn = 50,
15 | pn = 1,
16 | prefix,
17 | } = ctx.query
18 |
19 | const res = await service.singer.getArtistInfo({ category, rn, pn, prefix })
20 |
21 | ctx.body = res
22 | }
23 |
24 | // 歌手单曲
25 | async getArtistMusic () {
26 | const { ctx, service } = this
27 | const {
28 | artistid,
29 | rn = 30,
30 | pn = 1,
31 | } = ctx.query
32 |
33 | if (!artistid) {
34 | ctx.body = {
35 | code: 500,
36 | message: '参数错误',
37 | result: null,
38 | success: false,
39 | }
40 | return
41 | }
42 | const res = await service.singer.getArtistMusic({ artistid, rn, pn })
43 |
44 | ctx.body = res
45 | }
46 |
47 | // 获取歌手专辑
48 | async getArtistAlbum () {
49 | const { ctx, service } = this
50 | const {
51 | artistid,
52 | rn = 30,
53 | pn = 1,
54 | } = ctx.query
55 |
56 | if (!artistid) {
57 | ctx.body = {
58 | code: 500,
59 | message: '参数错误',
60 | result: null,
61 | success: false,
62 | }
63 | return
64 | }
65 |
66 | const res = await service.singer.getArtistAlbum({ artistid, rn, pn })
67 | ctx.body = res
68 | }
69 |
70 | // 获取歌手mv
71 |
72 | async getArtistMv () {
73 | const { ctx, service } = this
74 | const {
75 | artistid,
76 | rn = 30,
77 | pn = 1,
78 | } = ctx.query
79 |
80 | if (!artistid) {
81 | ctx.body = {
82 | code: 500,
83 | message: '参数错误',
84 | result: null,
85 | success: false,
86 | }
87 | return
88 | }
89 | const res = await service.singer.getArtistMv({ artistid, rn, pn })
90 | ctx.body = res
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/app/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 酷我音乐 API
9 |
10 |
11 |
12 | 酷我音乐 API
13 | 当你看到这个页面时,这个服务已经成功跑起来了~
14 | 查看文档
15 | 例子:
16 |
21 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/app/router.ts:
--------------------------------------------------------------------------------
1 | import { Application } from 'egg'
2 |
3 | export default (app: Application) => {
4 | const { controller, router } = app
5 | // 轮播图
6 | router.get('/kuwo/banner', controller.banner.index)
7 | // 评论
8 | router.get('/kuwo/comment', controller.comment.index)
9 | // 歌词
10 | router.get('/kuwo/lrc', controller.lrc.index)
11 | // 音乐信息
12 | router.get('/kuwo/musicInfo', controller.musicInfo.index)
13 | // 歌单音乐
14 | router.get('/kuwo/musicList', controller.musicList.index)
15 | // 音乐播放地址
16 | router.get('/kuwo/url', controller.playUrl.index)
17 | // 获取 mv 播放地址
18 | router.get('/kuwo/mv_url', controller.mv.getMvUrl)
19 | // 获取电台列表
20 | router.get('/kuwo/radio', controller.radio.index)
21 | // 获取歌手信息
22 | router.get('/kuwo/singer', controller.singer.getArtistInfo)
23 | // 获取歌手单曲
24 | router.get('/kuwo/singer/music', controller.singer.getArtistMusic)
25 | // 获取歌手专辑
26 | router.get('/kuwo/singer/album', controller.singer.getArtistAlbum)
27 | // 获取歌手mv
28 | router.get('/kuwo/singer/mv', controller.singer.getArtistMv)
29 | // 排行榜
30 | router.get('/kuwo/rank', controller.rank.index)
31 | // 排行榜音乐
32 | router.get('/kuwo/rank/musicList', controller.rank.rankMusicList)
33 | // 推荐榜单
34 | router.get('/kuwo/rank/rec_bangList', controller.rank.rankRecBangList)
35 | // 推荐歌单
36 | router.get('/kuwo/rec_gedan', controller.recGedan.index)
37 | // 推荐歌手
38 | router.get('/kuwo/rec_singer', controller.recSinger.index)
39 | // 歌单分类
40 | router.get('/kuwo/getTagList', controller.getTagList.index)
41 | // 默认歌单
42 | router.get('/kuwo/playList', controller.playList.index)
43 | // 专辑歌单
44 | router.get('/kuwo/albumInfo', controller.albumInfo.index)
45 | // 歌单分类
46 | router.get('/kuwo/playList/getTagPlayList', controller.playList.getTagPlayList)
47 | // 关键字搜索
48 | router.get('/kuwo/search/searchKey', controller.search.searchKey)
49 | // 单曲搜索
50 | router.get('/kuwo/search/searchMusicBykeyWord', controller.search.searchMusicBykeyWord)
51 | // 专辑搜索
52 | router.get('/kuwo/search/searchAlbumBykeyWord', controller.search.searchAlbumBykeyWord)
53 | // mv 搜索
54 | router.get('/kuwo/search/searchMvBykeyWord', controller.search.searchMvBykeyWord)
55 | // 歌单搜索
56 | router.get('/kuwo/search/searchPlayListBykeyWord', controller.search.searchPlayListBykeyWord)
57 | // 歌手搜索
58 | router.get('/kuwo/search/searchArtistBykeyWord', controller.search.searchArtistBykeyWord)
59 | }
60 |
--------------------------------------------------------------------------------
/app/schedule/logs.ts:
--------------------------------------------------------------------------------
1 | const fse = require('fs-extra')
2 |
3 | // https://www.eggjs.org/zh-CN/basics/schedule#%E5%AE%9A%E6%97%B6%E6%96%B9%E5%BC%8F
4 | module.exports = app => {
5 | return {
6 | schedule: {
7 | // 每 24 h 清除 logs
8 | interval: '86440s',
9 | type: 'all', // 指定所有的 worker 都需要执行
10 | },
11 | async task () {
12 | fse.emptydirSync(app.config?.logger?.dir, err => {
13 | if (!err) {
14 | return console.log('emptydirSync success')
15 | }
16 | })
17 | },
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/service/BaseService.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable jsdoc/require-param-type */
2 | import { Service } from 'egg'
3 | import { v4 as uuidv4 } from 'uuid'
4 |
5 | import { h, v, f, Cookie } from '../utils/secret'
6 |
7 | class BaseService extends Service {
8 | _headers (opts) {
9 | return {
10 | Cookie,
11 | Secret: h(Object(v)(f), f),
12 | Host: 'www.kuwo.cn',
13 | Referer: 'http://www.kuwo.cn/',
14 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36',
15 | ...opts,
16 | }
17 | }
18 |
19 | timeoutCount = 0
20 |
21 | async commonRequest (url, options) {
22 | const opts = {
23 | method: 'GET',
24 | dataType: 'json',
25 | timeout: 60000,
26 | ...options,
27 | headers: this._headers(options?.headers),
28 | }
29 | // eslint-disable-next-line @typescript-eslint/no-this-alias
30 | const _this = this
31 | return handleGetData(_this, url, opts)
32 | }
33 | }
34 |
35 | module.exports = BaseService
36 | /**
37 | * @param _this
38 | * @param url
39 | * @param opts
40 | */
41 | export function handleGetData (_this, url, opts) {
42 | const reqId = uuidv4()
43 | return _this.ctx.curl(`${url}&reqId=${reqId}`, opts).then(res => {
44 | _this.logger.info({
45 | req: Object.assign({}, opts, { ctx: undefined }),
46 | url,
47 | reqId,
48 | status: res.status,
49 | res: JSON.stringify(res.data),
50 | })
51 | _this.timeoutCount = 0
52 |
53 | return res.data
54 | }).catch(e => {
55 | _this.logger.info({
56 | req: Object.assign({}, opts, { ctx: undefined }),
57 | url,
58 | reqId,
59 | error: e,
60 | res: null,
61 | })
62 | // 失败自动重试
63 | if (_this.timeoutCount <= 2) {
64 | _this.timeoutCount++
65 | return handleGetData(_this, url, opts)
66 | }
67 |
68 | _this.timeoutCount = 0
69 |
70 | throw e
71 | })
72 | }
73 |
--------------------------------------------------------------------------------
/app/service/albumInfo.ts:
--------------------------------------------------------------------------------
1 | const BaseService = require('./BaseService')
2 |
3 | export default class AlbumInfo extends BaseService {
4 | async getList ({ albumId, pn, rn }) {
5 | return this.commonRequest(`http://www.kuwo.cn/api/www/album/albumInfo?albumId=${albumId}&pn=${pn}&rn=${rn}&httpsStatus=1`)
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/app/service/banner.ts:
--------------------------------------------------------------------------------
1 | const BaseService = require('./BaseService')
2 |
3 | export default class Banner extends BaseService {
4 | async List () {
5 | return this.commonRequest('http://www.kuwo.cn/api/www/banner/index/bannerList?&httpsStatus=1')
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/app/service/comment.ts:
--------------------------------------------------------------------------------
1 | const BaseService = require('./BaseService')
2 |
3 | export default class Comment extends BaseService {
4 | async List ({ sid, page, rows, uid, type, digest }) {
5 | const targetUrl = `http://www.kuwo.cn/comment?type=${type}&f=web&page=${page}&rows=${rows}&digest=${digest}&sid=${sid}&uid=${uid}&prod=newWeb&httpsStatus=1`
6 |
7 | let Ref = ''
8 | switch (digest) {
9 | case 15:
10 | return Ref = 'http://www.kuwo.cn/play_detail/' + encodeURIComponent(sid)
11 | case 7:
12 | return Ref = 'http://www.kuwo.cn/mvplay/' + encodeURIComponent(sid)
13 | case 8:
14 | return Ref = 'http://www.kuwo.cn/playlist_detail/' + encodeURIComponent(sid)
15 | case 2:
16 | return Ref = 'http://www.kuwo.cn/rankList'
17 | }
18 |
19 | return this.commonRequest(targetUrl, {
20 | headers: {
21 | Referer: Ref,
22 | },
23 | })
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/app/service/getTagList.ts:
--------------------------------------------------------------------------------
1 | const BaseService = require('./BaseService')
2 |
3 | export default class TagList extends BaseService {
4 | async index () {
5 | return this.commonRequest('http://www.kuwo.cn/api/www/playlist/getTagList?&httpsStatus=1')
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/app/service/lrc.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable node/prefer-global/process */
2 | const BaseService = require('./BaseService')
3 | const needle = require('needle')
4 | const process = require('process')
5 | const deflateRaw = require('zlib')
6 | const { inflate } = require('zlib')
7 | const iconv = require('iconv-lite')
8 |
9 | const bufkey = Buffer.from('yeelion')
10 | const bufkeylen = bufkey.length
11 | const buildParams = (id, isGetLyricx) => {
12 | let params = `user=12345,web,web,web&requester=localhost&req=1&rid=MUSIC_${id}`
13 | if (isGetLyricx) params += '&lrcx=1'
14 | const bufstr = Buffer.from(params)
15 | const bufstrlen = bufstr.length
16 | const output = new Uint16Array(bufstrlen)
17 | let i = 0
18 | while (i < bufstrlen) {
19 | let j = 0
20 | while (j < bufkeylen && i < bufstrlen) {
21 | // eslint-disable-next-line no-bitwise
22 | output[i] = bufkey[j] ^ bufstr[i]
23 | i++
24 | j++
25 | }
26 | }
27 | return Buffer.from(output).toString('base64')
28 | }
29 |
30 | const cancelHttp = requestObj => {
31 | if (!requestObj) return
32 | if (!requestObj.abort) return
33 | requestObj.abort()
34 | }
35 |
36 | const requestMsg = {
37 | fail: '请求异常,可以多试几次,若还是不行就换一首吧',
38 | unachievable: '哦No...接口无法访问了!',
39 | timeout: '请求超时',
40 | // unachievable: '哦No...接口无法访问了!已帮你切换到临时接口,重试下看能不能播放吧~',
41 | notConnectNetwork: '无法连接到服务器',
42 | cancelRequest: '取消http请求',
43 | }
44 |
45 | const request = (url, options, callback) => {
46 | let data
47 | if (options.body) {
48 | data = options.body
49 | } else if (options.form) {
50 | data = options.form
51 | // data.content_type = 'application/x-www-form-urlencoded'
52 | options.json = false
53 | } else if (options.formData) {
54 | data = options.formData
55 | // data.content_type = 'multipart/form-data'
56 | options.json = false
57 | }
58 | options.response_timeout = options.timeout
59 |
60 | return needle.request(options.method || 'get', url, data, options, (err, resp, body) => {
61 | if (!err) {
62 | body = resp.body = resp.raw.toString()
63 | try {
64 | resp.body = JSON.parse(resp.body)
65 | } catch (_) { }
66 | body = resp.body
67 | }
68 | callback(err, resp, body)
69 | }).request
70 | }
71 |
72 | const defaultHeaders = {
73 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
74 | }
75 |
76 | const handleDeflateRaw: any = data => new Promise((resolve, reject) => {
77 | deflateRaw(data, (err, buf) => {
78 | if (err) return reject(err)
79 | resolve(buf)
80 | })
81 | })
82 |
83 | const regx = /(?:\d\w)+/g
84 |
85 | const fetchData = async (url, method, {
86 | headers = {},
87 | format = 'json',
88 | timeout = 15000,
89 | ...options
90 | }, callback) => {
91 | headers = Object.assign({}, headers)
92 | const bHh = '624868746c'
93 | if (headers[bHh]) {
94 | const path = url.replace(/^https?:\/\/[\w.:]+\//, '/')
95 | let s = Buffer.from(bHh, 'hex').toString()
96 | s = s.replace(s.substr(-1), '')
97 | s = Buffer.from(s, 'base64').toString()
98 | const v = process.versions.app.split('-')[0].split('.').map(n => (n.length < 3 ? n.padStart(3, '0') : n)).join('')
99 | const v2 = process.versions.app.split('-')[1] || ''
100 | headers[s] = !s || `${(await handleDeflateRaw(Buffer.from(JSON.stringify(`${path}${v}`.match(regx), null, 1).concat(v)).toString('base64'))).toString('hex')}&${parseInt(v)}${v2}`
101 | delete headers[bHh]
102 | }
103 | return request(url, {
104 | ...options,
105 | method,
106 | headers: Object.assign({}, defaultHeaders, headers),
107 | timeout,
108 | json: format === 'json',
109 | }, (err, resp, body) => {
110 | if (err) return callback(err, null)
111 | callback(null, resp, body)
112 | })
113 | }
114 |
115 | const buildHttpPromose = (url, options) => {
116 | const obj: any = {
117 | isCancelled: false,
118 | }
119 | obj.promise = new Promise((resolve, reject) => {
120 | obj.cancelFn = reject
121 | // console.log(`\nsend request---${url}`)
122 | fetchData(url, options.method, options, (err, resp) => {
123 | obj.requestObj = null
124 | obj.cancelFn = null
125 | if (err) return reject(err)
126 | resolve(resp)
127 | }).then(ro => {
128 | obj.requestObj = ro
129 | if (obj.isCancelled) obj.cancelHttp()
130 | })
131 | })
132 | obj.cancelHttp = () => {
133 | if (!obj.requestObj) return obj.isCancelled = true
134 | cancelHttp(obj.requestObj)
135 | obj.requestObj = null
136 | obj.promise = obj.cancelHttp = null
137 | obj.cancelFn(new Error(requestMsg.cancelRequest))
138 | obj.cancelFn = null
139 | }
140 | return obj
141 | }
142 | const httpFetch = (url, options = { method: 'get' }) => {
143 | const requestObj = buildHttpPromose(url, options)
144 | requestObj.promise = requestObj.promise.catch(err => {
145 | if (err.message === 'socket hang up') {
146 | return Promise.reject(new Error(requestMsg.unachievable))
147 | }
148 | switch (err.code) {
149 | case 'ETIMEDOUT':
150 | case 'ESOCKETTIMEDOUT':
151 | return Promise.reject(new Error(requestMsg.timeout))
152 | case 'ENOTFOUND':
153 | return Promise.reject(new Error(requestMsg.notConnectNetwork))
154 | default:
155 | return Promise.reject(err)
156 | }
157 | })
158 | return requestObj
159 | }
160 | const lrcTools: any = {
161 | rxps: {
162 | wordLine: /^(\[\d{1,2}:.*\d{1,4}\])\s*(\S+(?:\s+\S+)*)?\s*/,
163 | tagLine: /\[(ver|ti|ar|al|offset|by|kuwo):\s*(\S+(?:\s+\S+)*)\s*\]/,
164 | wordTimeAll: /<(-?\d+),(-?\d+)(?:,-?\d+)?>/g,
165 | wordTime: /<(-?\d+),(-?\d+)(?:,-?\d+)?>/,
166 | },
167 | offset: 1,
168 | offset2: 1,
169 | isOK: false,
170 | lines: [],
171 | tags: [],
172 | getWordInfo (str, str2, prevWord) {
173 | const offset = parseInt(str)
174 | const offset2 = parseInt(str2)
175 | const startTime = Math.abs((offset + offset2) / (this.offset * 2))
176 | const endTime = Math.abs((offset - offset2) / (this.offset2 * 2)) + startTime
177 | if (prevWord) {
178 | if (startTime < prevWord.endTime) {
179 | prevWord.endTime = startTime
180 | if (prevWord.startTime > prevWord.endTime) {
181 | prevWord.startTime = prevWord.endTime
182 | }
183 | prevWord.newTimeStr = ''
184 | }
185 | }
186 | return {
187 | startTime,
188 | endTime,
189 | timeStr: '',
190 | }
191 | },
192 | parseLine (line) {
193 | if (line.length < 6) return
194 | let result = this.rxps.wordLine.exec(line)
195 | if (result) {
196 | const time = result[1]
197 | let words = result[2]
198 | if (words == null) {
199 | words = ''
200 | }
201 | const wordTimes = words.match(this.rxps.wordTimeAll)
202 | if (!wordTimes) return
203 | // console.log(wordTimes)
204 | let preTimeInfo
205 | for (const timeStr of wordTimes) {
206 | const result = this.rxps.wordTime.exec(timeStr)
207 | const wordInfo = this.getWordInfo(result[1], result[2], preTimeInfo)
208 | words = words.replace(timeStr, wordInfo.timeStr)
209 | if (preTimeInfo?.newTimeStr) words = words.replace(preTimeInfo.timeStr, preTimeInfo.newTimeStr)
210 | preTimeInfo = wordInfo
211 | }
212 | this.lines.push(time + words)
213 | return
214 | }
215 | result = this.rxps.tagLine.exec(line)
216 | if (!result) return
217 | if (result[1] === 'kuwo') {
218 | let content = result[2]
219 | if (content !== null && content.includes('][')) {
220 | content = content.substring(0, content.indexOf(']['))
221 | }
222 | const valueOf = parseInt(content, 8)
223 | this.offset = Math.trunc(valueOf / 10)
224 | this.offset2 = Math.trunc(valueOf % 10)
225 | if (this.offset === 0 || Number.isNaN(this.offset) || this.offset2 === 0 || Number.isNaN(this.offset2)) {
226 | this.isOK = false
227 | }
228 | } else {
229 | this.tags.push(line)
230 | }
231 | },
232 | parse (lrc) {
233 | // console.log(lrc)
234 | const lines = lrc.split(/\r\n|\r|\n/)
235 | const tools = Object.create(this)
236 | tools.isOK = true
237 | tools.offset = 1
238 | tools.offset2 = 1
239 | tools.lines = []
240 | tools.tags = []
241 | for (const line of lines) {
242 | if (!tools.isOK) throw new Error('failed')
243 | tools.parseLine(line)
244 | }
245 | if (!tools.lines.length) return ''
246 | let lrcs = tools.lines.join('\n')
247 | if (tools.tags.length) lrcs = `${tools.tags.join('\n')}\n${lrcs}`
248 | // console.log(lrcs)
249 | return lrcs
250 | },
251 | }
252 | const isGetLyricx = true
253 | const handleInflate = data => new Promise((resolve, reject) => {
254 | inflate(data, (err, result) => {
255 | if (err) return reject(err)
256 | resolve(result)
257 | })
258 | })
259 | const bufKey = Buffer.from('yeelion')
260 | const bufKeyLen = bufKey.length
261 | const decodeLyrics = async (buf, isGetLyricx) => {
262 | if (buf.toString('utf8', 0, 10) !== 'tp=content') return ''
263 | const lrcData: any = await handleInflate(buf.slice(buf.indexOf('\r\n\r\n') + 4))
264 | if (!isGetLyricx) return iconv.decode(lrcData, 'gb18030')
265 | const bufStr = Buffer.from(lrcData.toString(), 'base64')
266 | const bufStrLen = bufStr.length
267 | const output = new Uint16Array(bufStrLen)
268 | let i = 0
269 | while (i < bufStrLen) {
270 | let j = 0
271 | while (j < bufKeyLen && i < bufStrLen) {
272 | // eslint-disable-next-line no-bitwise
273 | output[i] = bufStr[i] ^ bufKey[j]
274 | i++
275 | j++
276 | }
277 | }
278 | return iconv.decode(Buffer.from(output), 'gb18030')
279 | }
280 | const timeExp = /^\[([\d:.]*)\]{1}/g
281 | const sortLrcArr = arr => {
282 | const lrcSet = new Set()
283 | const lrc: any = []
284 | const lrcT: any = []
285 | for (const item of arr) {
286 | if (lrcSet.has(item.time)) {
287 | if (lrc.length < 2) continue
288 | const tItem: any = lrc.pop()
289 | tItem.time = lrc[lrc.length - 1].time
290 | lrcT.push(tItem)
291 | lrc.push(item)
292 | } else {
293 | lrc.push(item)
294 | lrcSet.add(item.time)
295 | }
296 | }
297 | return {
298 | lrc,
299 | lrcT,
300 | }
301 | }
302 | const parseLrc = lrc => {
303 | const lines = lrc.split(/\r\n|\r|\n/)
304 | const tags: any = []
305 | const lrcArr: any = []
306 | for (let i = 0; i < lines.length; i++) {
307 | const line = lines[i].trim()
308 | const result = timeExp.exec(line)
309 | if (result) {
310 | let text = line.replace(timeExp, '').trim()
311 | let time = RegExp.$1
312 | if (/\.\d\d$/.test(time)) time += '0'
313 | const regexp = /<.*?>/g
314 | text = text.replace(regexp, '').replace(/\[by:.*?\](\n|$)/g, '').replace(/\[kuwo:.*?\](\n|$)/g, '')
315 | const times = time.split(':')
316 | time = (parseFloat(times[0]) * 60 + parseFloat(times[1])).toFixed(2)
317 | lrcArr.push({
318 | time,
319 | lineLyric: text,
320 | })
321 | } else if (lrcTools.rxps.tagLine.test(line)) {
322 | tags.push(line)
323 | }
324 | }
325 | const lrcInfo = sortLrcArr(lrcArr)
326 | return lrcInfo
327 | }
328 |
329 | const rendererInvoke = async params => {
330 | const lrc = await decodeLyrics(Buffer.from(params.lrcBase64, 'base64'), isGetLyricx)
331 | return Buffer.from(lrc).toString('base64')
332 | }
333 | const decodeLyric = base64Data => rendererInvoke(base64Data)
334 |
335 | export default class Lrc extends BaseService {
336 | async LrcRes (musicId) {
337 | const url = `http://newlyric.kuwo.cn/newlyric.lrc?${buildParams(musicId, isGetLyricx)}`
338 |
339 | const requestObj = httpFetch(url)
340 | requestObj.promise = requestObj.promise.then(({ statusCode, body, raw }) => {
341 | if (statusCode !== 200) {
342 | console.log(body)
343 | // 兼容 web 端请求
344 | return this.commonRequest(`http://m.kuwo.cn/newh5/singles/songinfoandlrc?musicId=${musicId}&httpsStatus=1`)
345 | // return Promise.reject(new Error(JSON.stringify(body)))
346 | }
347 | return decodeLyric({ lrcBase64: raw.toString('base64'), isGetLyricx }).then(base64Data => {
348 | let lrcInfo
349 | lrcInfo = parseLrc(Buffer.from(base64Data, 'base64').toString())
350 | try {
351 | lrcInfo = parseLrc(Buffer.from(base64Data, 'base64').toString())
352 | } catch (err) {
353 | return Promise.reject(new Error('Get lyric failed'))
354 | }
355 | const msg = {
356 | data: {
357 | lrclist: lrcInfo.lrc,
358 | },
359 | msg: '成功',
360 | status: 200,
361 | }
362 | return msg
363 | })
364 | })
365 | const asd = async () => {
366 | return await new Promise(resolve => {
367 | requestObj.promise.then(re => {
368 | resolve(re)
369 | })
370 | })
371 | }
372 |
373 | return await asd()
374 | }
375 | }
376 |
--------------------------------------------------------------------------------
/app/service/musicInfo.ts:
--------------------------------------------------------------------------------
1 | const BaseService = require('./BaseService')
2 |
3 | export default class MusicInfo extends BaseService {
4 | async getList (mid) {
5 | return this.commonRequest(`http://www.kuwo.cn/api/www/music/musicInfo?mid=${mid}&httpsStatus=1`)
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/app/service/musicList.ts:
--------------------------------------------------------------------------------
1 | const BaseService = require('./BaseService')
2 |
3 | export default class MusicList extends BaseService {
4 | async getList ({ pid, pn, rn }) {
5 | return this.commonRequest(`http://www.kuwo.cn/api/www/playlist/playListInfo?pid=${pid}&pn=${pn}&rn=${rn}&httpsStatus=1`)
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/app/service/mv.ts:
--------------------------------------------------------------------------------
1 | const BaseService = require('./BaseService')
2 |
3 | export default class Mv extends BaseService {
4 | async getMvUrl (rid) {
5 | const res = await this.commonRequest(`http://www.kuwo.cn/url?rid=${rid}&response=url&format=mp4%7Cmkv&type=convert_url&t=${Date.now()}&httpsStatus=1`, {
6 | headers: {
7 | Referer: 'http://www.kuwo.cn/mvs',
8 | },
9 | dataType: 'text',
10 | })
11 | return {
12 | code: 200,
13 | msg: 'success',
14 | url: res,
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/app/service/playList.ts:
--------------------------------------------------------------------------------
1 | const BaseService = require('./BaseService')
2 |
3 | export default class PlayList extends BaseService {
4 | async index ({ order, rn, pn }) {
5 | return await this.commonRequest(`http://www.kuwo.cn/api/www/classify/playlist/getRcmPlayList?pn=${pn}&rn=${rn}&order${order}&httpsStatus=1`)
6 | }
7 |
8 | // 歌单分类
9 | async getTagPlayList ({ id, rn, pn }) {
10 | return await this.commonRequest(`http://www.kuwo.cn/api/www/classify/playlist/getTagPlayList?pn=${pn}&rn=${rn}&id=${id}&httpsStatus=1`, {
11 | headers: {
12 | Referer: 'http://www.kuwo.cn/playlists',
13 | },
14 | })
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/service/playUrl.ts:
--------------------------------------------------------------------------------
1 | const BaseService = require('./BaseService')
2 |
3 | export default class PlayUrl extends BaseService {
4 | async getPlayUrl (mid, type = 'music', br = '128kmp3') {
5 | return this.commonRequest(`http://www.kuwo.cn/api/v1/www/music/playUrl?mid=${mid}&type=${type}&httpsStatus=1&plat=web_www&from=&br=${br}`)
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/app/service/radio.ts:
--------------------------------------------------------------------------------
1 | const BaseService = require('./BaseService')
2 |
3 | export default class Radio extends BaseService {
4 | async getRadio () {
5 | return this.commonRequest('http://www.kuwo.cn/api/www/radio/index/radioList?&httpsStatus=1', {
6 | headers: {
7 | Referer: 'http://www.kuwo.cn/rankList',
8 | },
9 | })
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/app/service/rank.ts:
--------------------------------------------------------------------------------
1 | const BaseService = require('./BaseService')
2 |
3 | export default class Rank extends BaseService {
4 | // 榜单
5 | async index () {
6 | return this.commonRequest('http://www.kuwo.cn/api/www/bang/bang/bangMenu?&httpsStatus=1')
7 | }
8 |
9 | // 排行榜音乐
10 | async getRankMusicList ({ bangId, pn, rn }) {
11 | return this.commonRequest(`http://www.kuwo.cn/api/www/bang/bang/musicList?bangId=${bangId}&pn=${pn}&rn=${rn}&httpsStatus=1`, {
12 | headers: {
13 | Referer: 'http://www.kuwo.cn/rankList',
14 | },
15 | })
16 | }
17 |
18 | // 推荐榜单 --首页
19 | async getRecBangList () {
20 | return this.commonRequest('http://www.kuwo.cn/api/www/bang/index/bangList?&httpsStatus=1', {
21 | headers: {
22 | Referer: 'http://www.kuwo.cn/rankList',
23 | },
24 | })
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/service/recGedan.ts:
--------------------------------------------------------------------------------
1 | const BaseService = require('./BaseService')
2 |
3 | export default class RecGedan extends BaseService {
4 | async index ({ id, pn, rn }) {
5 | return this.commonRequest(`http://www.kuwo.cn/api/www/rcm/index/playlist?id=${id}&pn=${pn}&rn=${rn}&httpsStatus=1`)
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/app/service/recSinger.ts:
--------------------------------------------------------------------------------
1 | const BaseService = require('./BaseService')
2 |
3 | export default class RecSinger extends BaseService {
4 | async index ({ category, pn, rn }) {
5 | return this.commonRequest(`http://www.kuwo.cn/api/www/artist/artistInfo?category=${category}&pn=${pn}&rn=${rn}&httpsStatus=1`)
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/app/service/search.ts:
--------------------------------------------------------------------------------
1 | const BaseService = require('./BaseService')
2 |
3 | export default class Search extends BaseService {
4 | // 关键字搜索
5 | async searchKey (key) {
6 | return this.commonRequest(`http://www.kuwo.cn/api/www/search/searchKey?key=${key}&httpsStatus=1`)
7 | }
8 |
9 | // 单曲搜索
10 | async searchMusicBykeyWord ({ key, pn, rn }) {
11 | return this.commonRequest(`http://www.kuwo.cn/api/www/search/searchMusicBykeyWord?key=${key}&pn=${pn}&rn=${rn}&httpsStatus=1`)
12 | }
13 |
14 | // 专辑搜索
15 | async searchAlbumBykeyWord ({ key, pn, rn }) {
16 | return this.commonRequest(`http://www.kuwo.cn/api/www/search/searchAlbumBykeyWord?key=${key}&pn=${pn}&rn=${rn}&httpsStatus=1`)
17 | }
18 |
19 | // mv 搜索
20 | async searchMvBykeyWord ({ key, pn, rn }) {
21 | return this.commonRequest(`http://www.kuwo.cn/api/www/search/searchMvBykeyWord?key=${key}&pn=${pn}&rn=${rn}&httpsStatus=1`)
22 | }
23 |
24 | // 歌单搜索
25 | async searchPlayListBykeyWord ({ key, pn, rn }) {
26 | return this.commonRequest(`http://www.kuwo.cn/api/www/search/searchPlayListBykeyWord?key=${key}&pn=${pn}&rn=${rn}&httpsStatus=1`)
27 | }
28 |
29 | // 歌手搜索
30 | async searchArtistBykeyWord ({ key, pn, rn }) {
31 | return this.commonRequest(`http://www.kuwo.cn/api/www/search/searchArtistBykeyWord?key=${key}&pn=${pn}&rn=${rn}&httpsStatus=1`)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/service/singer.ts:
--------------------------------------------------------------------------------
1 | const BaseService = require('./BaseService')
2 |
3 | export default class Artist extends BaseService {
4 | // 获取歌手
5 | async getArtistInfo ({ category, rn, pn, prefix }) {
6 | return this.commonRequest(
7 | prefix
8 | ? `http://www.kuwo.cn/api/www/artist/artistInfo?category=${category}&pn=${pn}&rn=${rn}&prefix=${prefix}&httpsStatus=1`
9 | : `http://www.kuwo.cn/api/www/artist/artistInfo?category=${category}&pn=${pn}&rn=${rn}&httpsStatus=1`, {
10 | headers: {
11 | Referer: prefix ? 'http://www.kuwo.cn/singers' : undefined,
12 | },
13 | })
14 | }
15 |
16 | // 获取歌手单曲
17 | async getArtistMusic ({ artistid, rn, pn }) {
18 | return this.commonRequest(`http://www.kuwo.cn/api/www/artist/artistMusic?artistid=${artistid}&rn=${rn}&pn=${pn}&httpsStatus=1`, {
19 | headers: {
20 | Referer: 'http://www.kuwo.cn/singer_detail/' + encodeURIComponent(artistid),
21 | },
22 | })
23 | }
24 |
25 | // 获取歌手专辑
26 |
27 | async getArtistAlbum ({ artistid, rn, pn }) {
28 | return this.commonRequest(`http://www.kuwo.cn/api/www/artist/artistAlbum?artistid=${artistid}&rn=${rn}&pn=${pn}&httpsStatus=1`, {
29 | headers: {
30 | Referer: 'http://www.kuwo.cn/singer_detail/' + encodeURIComponent(artistid) + '/album',
31 | },
32 | })
33 | }
34 |
35 | // 获取歌手mv
36 | async getArtistMv ({ artistid, rn, pn }) {
37 | return this.commonRequest(`http://www.kuwo.cn/api/www/artist/artistMv?artistid=${artistid}&rn=${rn}&pn=${pn}&httpsStatus=1`, {
38 | headers: {
39 | Referer: 'http://www.kuwo.cn/singer_detail/' + encodeURIComponent(artistid) + '/mv',
40 | },
41 | })
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/utils/secret.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-unused-expressions */
2 | /**
3 | * @param t
4 | * @param e
5 | */
6 | function h (t, e) {
7 | if (e == null || e.length <= 0) {
8 | return console.log('Please enter a password with which to encrypt the message.'),
9 | null;
10 | }
11 | for (var n = '', i = 0; i < e.length; i++) { n += e.charCodeAt(i).toString(); }
12 | const r = Math.floor(n.length / 5);
13 | const o = parseInt(n.charAt(r) + n.charAt(2 * r) + n.charAt(3 * r) + n.charAt(4 * r) + n.charAt(5 * r));
14 | const l = Math.ceil(e.length / 2);
15 | const c = Math.pow(2, 31) - 1;
16 | if (o < 2) {
17 | return console.log('Algorithm cannot find a suitable hash. Please choose a different password. \nPossible considerations are to choose a more complex or longer password.'),
18 | null;
19 | }
20 | let d = Math.round(1e9 * Math.random()) % 1e8;
21 | for (n += d; n.length > 10;) { n = (parseInt(n.substring(0, 10)) + parseInt(n.substring(10, n.length))).toString(); }
22 | n = (o * n + l) % c;
23 | let h = '';
24 | let f = '';
25 | for (i = 0; i < t.length; i++) {
26 | f += (h = parseInt(t.charCodeAt(i) ^ Math.floor(n / c * 255))) < 16 ? '0' + h.toString(16) : h.toString(16),
27 | n = (o * n + l) % c;
28 | }
29 | for (d = d.toString(16); d.length < 8;) { d = '0' + d; }
30 | return f += d;
31 | }
32 |
33 | const f = 'Hm_Iuvt_cdb524f42f0ce19b169b8072123a4727';
34 | const Cookie = 'Hm_lvt_cdb524f42f0ce19b169a8071123a4797=1689780885000; _ga=GA1.2.259721034.1689780885000; _gid=GA1.2.1715768254.1689780885000; Hm_lpvt_cdb524f42f0ce19b169a8071123a4797=1689780885000; _ga_ETPBRPM9ML=GS1.2.1689780885000.2.0.1689780885000.60.0.0; Hm_Iuvt_cdb524f42f0ce19b169b8072123a4727=3MiWHX6n8Zr8sN48sF3dccyTWjZ54Hxy';
35 | const v = function (t = f) {
36 | // 使用 cookie
37 | const e = Cookie;
38 | let n = e.indexOf(t + '=');
39 | if (n !== -1) {
40 | n = n + t.length + 1;
41 | let r = e.indexOf(';', n);
42 | return r === -1 && (r = e.length),
43 | unescape(e.substring(n, r));
44 | }
45 | return null;
46 | }
47 |
48 | let l = '';
49 | l = Object(v)(f);
50 |
51 | module.exports = {
52 | h,
53 | v,
54 | f,
55 | Cookie,
56 | };
57 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | environment:
2 | matrix:
3 | - nodejs_version: '8'
4 |
5 | install:
6 | - ps: Install-Product node $env:nodejs_version
7 | - npm i npminstall && node_modules\.bin\npminstall
8 |
9 | test_script:
10 | - node --version
11 | - npm --version
12 | - npm run test
13 |
14 | build: off
15 |
--------------------------------------------------------------------------------
/config/config.default.ts:
--------------------------------------------------------------------------------
1 | import { EggAppConfig, EggAppInfo, PowerPartial } from 'egg'
2 | const path = require('path')
3 | const fs = require('fs')
4 |
5 | export default (appInfo: EggAppInfo) => {
6 | const config = {} as PowerPartial
7 |
8 | // override config from framework / plugin
9 | // use for cookie sign key, should change to your own and keep security
10 | config.keys = appInfo.name + '_1618050800795_6113'
11 |
12 | // add your egg config in here
13 | config.middleware = []
14 |
15 | // add your special config in here
16 | const bizConfig = {
17 | sourceUrl: `https://github.com/eggjs/examples/tree/master/${appInfo.name}`,
18 | headers: {
19 | /**
20 | * _ga=GA1.2.675600123.1604474404; gid=1c56e740-1027-41a2-bbd5-40c0372f1af8; JSESSIONID=1wc56uuw4qocl1jef6lxbdm7gq; uname3=%u90B1%u8000%u6D2A; t3kwid=485455771; userid=485455771; websid=155282790;
21 | * pic3="http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eqNeken3rBYSfXTn2Nq6uOm8aumOqIqsj7ibvM3bx9ibUmTp644d0hO4qftyS3iabA48AUzMdqauqaHQ/132";
22 | * t3=weixin; Hm_lvt_cdb524f42f0ce19b169a8071123a4797=1622942674,1623162158,1623249078,1623338422; Hm_lpvt_cdb524f42f0ce19b169a8071123a4797=1623338422;
23 | * _gid=GA1.2.85752388.1623338422; kw_token=9ACQW0P7QIP; _gat=1
24 | */
25 | Cookie: 'Hm_lvt_cdb524f42f0ce19b169a8071123a4797=1623339177,1623339183; _ga=GA1.2.1195980605.1579367081; Hm_lpvt_cdb524f42f0ce19b169a8071123a4797=1623339982; kw_token=3E7JFQ7MRPL; _gid=GA1.2.747985028.1623339179; _gat=1',
26 | csrf: '3E7JFQ7MRPL',
27 | Host: 'www.kuwo.cn',
28 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36',
29 | },
30 | }
31 |
32 | config.security = {
33 | csrf: {
34 | enable: false,
35 | },
36 | domainWhiteList: ['*'],
37 | }
38 |
39 | // 跨域支持
40 | config.cors = {
41 | origin: '*',
42 | allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS',
43 | }
44 | // 端口
45 | config.cluster = {
46 | listen: {
47 | path: '',
48 | port: 7002,
49 | hostname: '127.0.0.1',
50 | },
51 | }
52 |
53 | config.siteFile = {
54 | '/': fs.readFileSync(path.join(appInfo.baseDir, 'app/public/index.html')),
55 | }
56 |
57 | // the return config will combines to EggAppConfig
58 | return {
59 | ...config,
60 | ...bizConfig,
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/config/config.local.ts:
--------------------------------------------------------------------------------
1 | import { EggAppConfig, PowerPartial } from 'egg'
2 |
3 | export default () => {
4 | const config: PowerPartial = {}
5 | return config
6 | }
7 |
--------------------------------------------------------------------------------
/config/config.prod.ts:
--------------------------------------------------------------------------------
1 | import { EggAppConfig, PowerPartial } from 'egg'
2 |
3 | export default () => {
4 | const config: PowerPartial = {
5 | proxy: true,
6 | }
7 | return config
8 | }
9 |
--------------------------------------------------------------------------------
/config/plugin.ts:
--------------------------------------------------------------------------------
1 | import { EggPlugin } from 'egg'
2 |
3 | const plugin: EggPlugin = {
4 | // static: true,
5 | // nunjucks: {
6 | // enable: true,
7 | // package: 'egg-view-nunjucks',
8 | // },
9 | // assets :{
10 | // enable: true,
11 | // package: 'egg-view-assets',
12 | // },
13 | cors: {
14 | enable: true,
15 | package: 'egg-cors',
16 | },
17 |
18 | }
19 |
20 | export default plugin
21 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qyhqiu/kuwoMusicApi/e8e720b90b4d7e3052078a3380906f2b3349e388/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # 酷我音乐API
2 |
3 |
4 | ## 接口文档
5 |
6 | #### 播放链接( `music`| `mv`) 统一接口
7 |
8 | 接口: `/kuwo/url?mid=162457325&type=music&br=128kmp3` | `/kuwo/url?mid=162457325&type=mv`
9 |
10 | | 参数 | 说明 | 是否必须 | 示例 |
11 | | ----- | ------- | -------- | ---------------- |
12 | | `mid` | 歌曲 id | 是 | `mid` =162457325 |
13 | | `type` | 播放类型 | 默认 music | `type`= music / mv |
14 | | `br` | 播放音质:可选 128kmp3、192kmp3、320kmp3 | 默认 128kmp3 | `br`= 128kmp3 |
15 |
16 |
17 |
18 | #### 歌词
19 |
20 | 示例: `/kuwo/lrc?musicId=162457325`
21 |
22 | | 参数 | 说明 | 是否必须 | 示例 |
23 | | --------- | ------ | -------- | ------------------- |
24 | | `musicId` | 音乐id | 是 | `musicId`=162457325 |
25 | | | | | |
26 |
27 |
28 |
29 | #### 搜索
30 |
31 | 接口: `/kuwo/search/searchMusicBykeyWord?key=等你归来`
32 |
33 | > 1. `/kuwo/search/searchKey?key=xxx`
34 | > 2. `/kuwo/search/searchMusicBykeyWord?key=xxx`
35 | > 3. `/kuwo/search/searchAlbumBykeyWord?key=xxx`
36 | > 4. `/kuwo/search/searchMvBykeyWord?key=xxx`
37 | > 5. `/kuwo/search/searchPlayListBykeyWord?key=xxx`
38 | > 6. `/kuwo/search/searchArtistBykeyWord?key=xxx`
39 |
40 | 参数 : `searchKey` 搜索提示 `searchMusicBykeyWord` 单曲`searchAlbumBykeyWord` 专辑`searchMvBykeyWord` mv `searchPlayListBykeyWord` 歌单 `searchArtistBykeyWord` 歌手
41 |
42 | | 参数 | 说明 | 是否必须 | 示例 |
43 | | ----- | ---------- | ---------- | -------------- |
44 | | `key` | 搜索关键字 | 是 | key = 等你归来 |
45 | | `pn` | 页数 | 否 默认 1 | `pn `= 2 |
46 | | `rn` | 每页数量 | 否 默认 30 | `rn `= 15 |
47 |
48 |
49 |
50 | #### 轮播图
51 |
52 | 接口: `/kuwo/banner`
53 |
54 | ```json
55 | {
56 | "code": 200,
57 | "curTime": 1581397195179,
58 | "data": [
59 | {
60 | "id": 1,
61 | "pic": "http://kwimg4.kuwo.cn/star/upload/47/52/1557311466098_.png",
62 | "priority": 1,
63 | "url": "http://down.kuwo.cn/mbox/kwmusic_web_1.exe"
64 | },
65 | {
66 | "id": 18,
67 | "pic": "http://kwimg2.kuwo.cn/star/upload/12/15/1581389006583_.jpg",
68 | "priority": 2,
69 | "url": "http://www.kuwo.cn/album_detail/12554981"
70 | },
71 | {
72 | "id": 10,
73 | "pic": "http://kwimg4.kuwo.cn/star/upload/76/42/1581305352340_.jpg",
74 | "priority": 3,
75 | "url": "http://www.kuwo.cn/album_detail/12720213"
76 | },
77 | {
78 | "id": 9,
79 | "pic": "http://kwimg3.kuwo.cn/star/upload/72/82/1581305211162_.jpg",
80 | "priority": 4,
81 | "url": "http://www.kuwo.cn/album_detail/7662111"
82 | },
83 | {
84 | "id": 11,
85 | "pic": "http://kwimg2.kuwo.cn/star/upload/2/94/1581092599880_.jpg",
86 | "priority": 5,
87 | "url": "http://www.kuwo.cn/playlist_detail/2952464073"
88 | },
89 | {
90 | "id": 19,
91 | "pic": "http://kwimg4.kuwo.cn/star/upload/1/51/1581220186723_.jpg",
92 | "priority": 6,
93 | "url": "http://www.kuwo.cn/playlist_detail/2950090991"
94 | },
95 | {
96 | "id": 20,
97 | "pic": "http://kwimg3.kuwo.cn/star/upload/34/71/1580525755117_.jpg",
98 | "priority": 7,
99 | "url": "http://www.kuwo.cn/playlist_detail/2948964151"
100 | },
101 | {
102 | "id": 7,
103 | "pic": "http://kwimg1.kuwo.cn/star/upload/6/66/1579069807332_.jpg",
104 | "priority": 8,
105 | "url": "http://jx.kuwo.cn/KuwoLive/OpenLiveRoomLinkForKw?from=1001004053"
106 | }
107 | ],
108 | "msg": "success",
109 | "profileId": "site",
110 | "reqId": "be53e268b062aa76eca763beb8a7f071"
111 | }
112 | ```
113 |
114 |
115 |
116 | #### 评论
117 |
118 | 接口: `/kuwo/comment?sid=80958029&type=get_rec_comment&page=1&rows=30&digest=15`
119 |
120 | | 参数 | 说明 | 是否必须 | 示例 |
121 | | -------- | --------------------------------------------------- | -------- | ---------------------- |
122 | | `sid` | 评论类型 id | 是 | `sid`=80958029 |
123 | | `page` | 页数 | | 默认1 |
124 | | `rows` | 每页条数 | | 默认 30 |
125 | | `type` | `get_rec_comment` 热门评论 `get_comment` 最新评论 | | 默认 `get_rec_comment` |
126 | | `digest` | 15 歌曲 2 排行榜 7 mv评论 8 歌单评论 | | 默认 `15` |
127 |
128 | ### 歌单
129 |
130 | #### - 推荐歌单
131 |
132 | 接口:`/kuwo/rec_gedan`
133 |
134 | | 參數 | 説明 | 是否必須 | 示例 |
135 | | ---- | -------- | -------- | ----- |
136 | | `rn` | 分页 | | 默认1 |
137 | | `pn` | 每页数量 | | 默认5 |
138 |
139 | ```json
140 | list: [
141 | 0: {img: "http://img1.kwcdn.kuwo.cn/star/userpl2015/10/13/1582042149394_132026710_150.jpg", uname: "",…}
142 | img: "http://img1.kwcdn.kuwo.cn/star/userpl2015/10/13/1582042149394_132026710_150.jpg"
143 | uname: ""
144 | img700: "http://img1.kwcdn.kuwo.cn/star/userpl2015/10/13/1582042149394_132026710_700.jpg"
145 | img300: "http://img1.kwcdn.kuwo.cn/star/userpl2015/10/13/1582042149394_132026710b.jpg"
146 | userName: ""
147 | img500: "http://img1.kwcdn.kuwo.cn/star/userpl2015/10/13/1582042149394_132026710_500.jpg"
148 | total: 548
149 | name: "每日最新单曲推荐"
150 | listencnt: 197482809
151 | id: 1082685104
152 | tag: ""
153 | musicList: []
154 | desc: ""
155 | info: "该专辑先后邀请孟文豪、董楠、良朋、崔恕、赵佳霖等国内知名音乐人,分别围绕致敬生命、致敬逆行者、致敬祖国等角度进行创作,并将邀请韩磊、佟丽娅等国内著名歌手及青年演员倾情演唱"
156 | 1: {img: "http://img1.kwcdn.kuwo.cn/star/userpl2015/15/53/1482267851912_54513215_500.jpg", uname: "熙姊。",…}
157 | img: "http://img1.kwcdn.kuwo.cn/star/userpl2015/15/53/1482267851912_54513215_500.jpg"
158 | uname: "熙姊。"
159 | img700: "http://img1.kwcdn.kuwo.cn/star/userpl2015/15/53/1482267851912_54513215_700.jpg"
160 | img300: "http://img1.kwcdn.kuwo.cn/star/userpl2015/15/53/1482267851912_54513215b.jpg"
161 | userName: "熙姊。"
162 | img500: "http://img1.kwcdn.kuwo.cn/star/userpl2015/15/53/1482267851912_54513215_500.jpg"
163 | total: 51
164 | name: "<周杰伦>饶舌Rap快歌最全合集"
165 | listencnt: 636642
166 | id: 2073771443
167 | musicList: []
168 | desc: ""
169 | info: "觉得自己舌头不错的勇士,快来挑战吧!"
170 | }
171 | ,…]
172 | ```
173 |
174 |
175 |
176 | #### - 歌单音乐
177 |
178 | 接口:`/kuwo/musicList`
179 |
180 | 示例:`/kuwo/musicList?pid=1082685104`
181 |
182 | | 参数 | 说明 | 是否必须 | 示例 |
183 | | ----- | -------- | -------- | ------------- |
184 | | `pid` | 歌单id | 是 | id=1082685104 |
185 | | `rn` | 分页 | | 默认1 |
186 | | `pn` | 每页数量 | | 默认30 |
187 |
188 |
189 |
190 | #### - 默认歌单
191 |
192 | 接口: `/kuwo/playList`
193 |
194 | 示例:`/kuwo/playList?order=new&rn=30&pn=1`
195 |
196 | | 参数 | 说明 | 是否必须 | 示例 |
197 | | ----- | ------------------ | -------- | -------- |
198 | | order | new 最新 hot 最热 | | 默认 new |
199 | | `rn` | 每页条数 | | 默认 30 |
200 | | `pn` | 页数 | | 默认 1 |
201 |
202 | ### - 歌手专辑 歌单
203 |
204 | 接口: `/kuwo/albumInfo`
205 |
206 | 示例: `/kuwo/albumInfo?albumId=49449&rn=30&pn=1`
207 |
208 | | 参数 | 说明 | 是否必须 | 示例 |
209 | | --------- | -------- | -------- | --------------- |
210 | | `albumId` | 专辑 id | 是 | `albumId`=49449 |
211 | | `rn` | 每页条数 | | 默认 30 |
212 | | `pn` | 页数 | | 默认 1 |
213 |
214 | ```json
215 | {
216 | "code": 200,
217 | "curTime": 1636723164284,
218 | "data": {
219 | "playCnt": 126806,
220 | "artist": "周杰伦",
221 | "releaseDate": "2010-05-18",
222 | "album": "跨时代",
223 | "albumid": 49449,
224 | "pay": 0,
225 | "artistid": 336,
226 | "pic": "https://img2.kuwo.cn/star/albumcover/300/98/24/205164915.jpg",
227 | "isstar": 0,
228 | "total": 12,
229 | "content_type": "0",
230 | "albuminfo": "跨越自己 创新时代 周杰伦 2010第十辑【跨时代】 跨乐古今.飞越想象.创意绝伦.超时感动 华语乐坛天王周杰伦每次带给大家的全新音乐每每引领话题,而专辑封面更是备受瞩目!第10张个人专辑「跨时代」在5月18日全亚洲同步发行,随着封面的曝光,彷佛预告了一张经典专辑即将诞生!周杰伦穿着中古世纪的服装,化身吸血鬼王子,穿梭时空来到现代,在古堡的天台上,他忧郁凝视远方,只要音乐存在,吸血鬼王子就能跨越时代闻乐苏醒,这也是这张专辑的音乐精神所在,当初创作音乐的初衷与坚持依然不变,就像周杰伦在他第一张同名专辑内页里写的:「希望我现在认为很屌的杰伦专辑,以后听也能一样佩服自己!」事实上周董对自己的音乐依然充满自信,希望音乐可以跨时代继续感动所有人。 封面里周杰伦一身中古世纪的服装令人惊艳!尤其是黑色蕾丝斗篷披风有别于一般人对吸血鬼的印象,造型师表示今年流行的蕾丝大多运用在女生上,但我们大胆的用在杰伦的身上却有很自然又突出的效果!而这次拍摄封面与内页的场景就是在「跨时代」MV的场景里拍摄,片场租了两个大棚搭建了三个场景,光封面拍照的场景动辄就花费300万!为达到视觉上的三维效果,「跨时代」专辑铁盒精装版的封面将制作成3D影像,让歌迷有身历其境的感受! 「超人不会飞」道出十年心路历程 各方意见让人疲惫,压力大但不能流泪 超人周董还要继续飞 「超人不会飞」 自嘲好人好事代表 需接受众人检视 伟大无奈的杰伦就算压力大 仍坚持硬汉形象 因为「超人不能流眼泪」 天王周杰伦一举一动均引起华语乐坛骚动,2008年发行【魔杰座】专辑,顺利获得金曲奖八项入围肯定,并拿下叁项大奖,成绩裴然。在广大乐迷引颈期盼将近两年后,周杰伦终于要在今年5月推出个人第十张全新音乐大碟, 4月26日早上10点新专辑首波主打歌「超人不会飞」完整首播,第一时间与听友分享周杰伦的最新创作。 主打歌「超人不会飞」 自嘲好人好事代表 需接受众人检视 就算压力大 仍坚持硬汉形象 因为「超人不能流眼泪」 新歌「超人不会飞」,由周杰伦填词谱曲兼制作,以抒情曲风缓缓唱出出道十年心路历程与感慨。歌词中谈到自己带来的社会以及音乐现象,更自嘲自己成为好人好事代表,需接受众人检视,自然散发出独特的周式幽默风格,令人会心一笑,周杰伦表示「一般带有批判意味的歌词都会用重节奏或饶舌曲风包装,但我就是要用抒情曲风带给大家全新感受!」 歌名「超人不会飞」,自嘲没有特异功能的周杰伦,只有梦想与坚持去做音乐!没想到他的歌曲可以变成学校教材,他的一举一动都变成社会榜样,每个人都在帮他计算收视率、票房与奖项,他的形象、表情、言行举止、甚至开的车、住的楼,还是专辑有没有夺冠?电影有没有创下高票房?通通被挖出来仔细检验,如同歌词所说,妈妈说很多事别太计较,只是使命感找到了我,我睡不着。他也不禁大唱,我到底是一个创作歌手,还是好人好事代表。周杰伦表示这样的生活难免觉得累,也需要呼吸空间,但歌词同时也透露「不要问我哭过了没,因为超人不能流眼泪!」依然展现十足铁汉个性。",
231 | "lang": "普通话",
232 | "musicList": [
233 | {
234 | "musicrid": "MUSIC_728673",
235 | "barrage": "0",
236 | "artist": "周杰伦",
237 | "mvpayinfo": {
238 | "play": 0,
239 | "vid": 10307548,
240 | "down": 0
241 | },
242 | "pic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
243 | "isstar": 0,
244 | "rid": 728673,
245 | "duration": 194,
246 | "score100": "57",
247 | "content_type": "0",
248 | "track": 1,
249 | "hasLossless": true,
250 | "hasmv": 1,
251 | "releaseDate": "2010-05-18",
252 | "album": "跨时代",
253 | "albumid": 49449,
254 | "pay": "16711935",
255 | "artistid": 336,
256 | "albumpic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
257 | "originalsongtype": 1,
258 | "songTimeMinutes": "03:14",
259 | "isListenFee": true,
260 | "pic120": "https://img2.kuwo.cn/star/albumcover/120/98/24/205164915.jpg",
261 | "name": "跨时代",
262 | "online": 1,
263 | "payInfo": {
264 | "play": "1111",
265 | "download": "1111",
266 | "local_encrypt": "1",
267 | "limitfree": 0,
268 | "cannotDownload": 0,
269 | "refrain_start": 48031,
270 | "listen_fragment": "1",
271 | "refrain_end": 69528,
272 | "cannotOnlinePlay": 0,
273 | "feeType": {
274 | "song": "1",
275 | "vip": "1"
276 | },
277 | "down": "1111"
278 | }
279 | },
280 | {
281 | "musicrid": "MUSIC_728675",
282 | "barrage": "0",
283 | "artist": "周杰伦",
284 | "mvpayinfo": {
285 | "play": 0,
286 | "vid": 10307415,
287 | "down": 0
288 | },
289 | "pic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
290 | "isstar": 0,
291 | "rid": 728675,
292 | "duration": 282,
293 | "score100": "67",
294 | "content_type": "0",
295 | "track": 2,
296 | "hasLossless": true,
297 | "hasmv": 1,
298 | "releaseDate": "2010-05-18",
299 | "album": "跨时代",
300 | "albumid": 49449,
301 | "pay": "16711935",
302 | "artistid": 336,
303 | "albumpic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
304 | "originalsongtype": 1,
305 | "songTimeMinutes": "04:42",
306 | "isListenFee": true,
307 | "pic120": "https://img2.kuwo.cn/star/albumcover/120/98/24/205164915.jpg",
308 | "name": "说了再见",
309 | "online": 1,
310 | "payInfo": {
311 | "play": "1111",
312 | "download": "1111",
313 | "local_encrypt": "1",
314 | "limitfree": 0,
315 | "cannotDownload": 0,
316 | "refrain_start": 93238,
317 | "listen_fragment": "1",
318 | "refrain_end": 124785,
319 | "cannotOnlinePlay": 0,
320 | "feeType": {
321 | "song": "1",
322 | "vip": "1"
323 | },
324 | "down": "1111"
325 | }
326 | },
327 | {
328 | "musicrid": "MUSIC_728677",
329 | "barrage": "0",
330 | "artist": "周杰伦",
331 | "mvpayinfo": {
332 | "play": 0,
333 | "vid": 10307419,
334 | "down": 0
335 | },
336 | "pic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
337 | "isstar": 0,
338 | "rid": 728677,
339 | "duration": 262,
340 | "score100": "77",
341 | "content_type": "0",
342 | "track": 3,
343 | "hasLossless": true,
344 | "hasmv": 1,
345 | "releaseDate": "2010-05-18",
346 | "album": "跨时代",
347 | "albumid": 49449,
348 | "pay": "16711935",
349 | "artistid": 336,
350 | "albumpic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
351 | "originalsongtype": 1,
352 | "songTimeMinutes": "04:22",
353 | "isListenFee": true,
354 | "pic120": "https://img2.kuwo.cn/star/albumcover/120/98/24/205164915.jpg",
355 | "name": "烟花易冷",
356 | "online": 1,
357 | "payInfo": {
358 | "play": "1111",
359 | "download": "1111",
360 | "local_encrypt": "1",
361 | "limitfree": 0,
362 | "cannotDownload": 0,
363 | "refrain_start": 61374,
364 | "listen_fragment": "1",
365 | "refrain_end": 112162,
366 | "cannotOnlinePlay": 0,
367 | "feeType": {
368 | "song": "1",
369 | "vip": "1"
370 | },
371 | "down": "1111"
372 | }
373 | },
374 | {
375 | "musicrid": "MUSIC_728674",
376 | "barrage": "0",
377 | "artist": "周杰伦",
378 | "mvpayinfo": {
379 | "play": 0,
380 | "vid": 10307549,
381 | "down": 0
382 | },
383 | "nationid": "0",
384 | "pic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
385 | "isstar": 0,
386 | "rid": 728674,
387 | "duration": 238,
388 | "score100": "50",
389 | "content_type": "0",
390 | "track": 4,
391 | "hasLossless": true,
392 | "hasmv": 1,
393 | "releaseDate": "2010-05-18",
394 | "album": "跨时代",
395 | "albumid": 49449,
396 | "pay": "16711935",
397 | "artistid": 336,
398 | "albumpic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
399 | "originalsongtype": 1,
400 | "songTimeMinutes": "03:58",
401 | "isListenFee": true,
402 | "pic120": "https://img2.kuwo.cn/star/albumcover/120/98/24/205164915.jpg",
403 | "name": "免费教学录影带",
404 | "online": 1,
405 | "payInfo": {
406 | "play": "1111",
407 | "download": "1111",
408 | "local_encrypt": "1",
409 | "limitfree": 0,
410 | "cannotDownload": 0,
411 | "refrain_start": 61695,
412 | "listen_fragment": "1",
413 | "refrain_end": 83273,
414 | "cannotOnlinePlay": 0,
415 | "feeType": {
416 | "song": "1",
417 | "vip": "1"
418 | },
419 | "down": "1111"
420 | }
421 | },
422 | {
423 | "musicrid": "MUSIC_728669",
424 | "barrage": "0",
425 | "artist": "周杰伦",
426 | "mvpayinfo": {
427 | "play": 0,
428 | "vid": 10307545,
429 | "down": 0
430 | },
431 | "pic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
432 | "isstar": 0,
433 | "rid": 728669,
434 | "duration": 252,
435 | "score100": "59",
436 | "content_type": "0",
437 | "track": 5,
438 | "hasLossless": true,
439 | "hasmv": 1,
440 | "releaseDate": "2010-05-18",
441 | "album": "跨时代",
442 | "albumid": 49449,
443 | "pay": "16711935",
444 | "artistid": 336,
445 | "albumpic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
446 | "originalsongtype": 1,
447 | "songTimeMinutes": "04:12",
448 | "isListenFee": true,
449 | "pic120": "https://img2.kuwo.cn/star/albumcover/120/98/24/205164915.jpg",
450 | "name": "好久不见",
451 | "online": 1,
452 | "payInfo": {
453 | "play": "1111",
454 | "download": "1111",
455 | "local_encrypt": "1",
456 | "limitfree": 0,
457 | "cannotDownload": 0,
458 | "refrain_start": 89054,
459 | "listen_fragment": "1",
460 | "refrain_end": 129689,
461 | "cannotOnlinePlay": 0,
462 | "feeType": {
463 | "song": "1",
464 | "vip": "1"
465 | },
466 | "down": "1111"
467 | }
468 | },
469 | {
470 | "musicrid": "MUSIC_726836",
471 | "barrage": "0",
472 | "artist": "周杰伦",
473 | "mvpayinfo": {
474 | "play": 0,
475 | "vid": 10307544,
476 | "down": 0
477 | },
478 | "pic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
479 | "isstar": 0,
480 | "rid": 726836,
481 | "duration": 256,
482 | "score100": "65",
483 | "content_type": "0",
484 | "track": 6,
485 | "hasLossless": true,
486 | "hasmv": 1,
487 | "releaseDate": "2010-05-18",
488 | "album": "跨时代",
489 | "albumid": 49449,
490 | "pay": "16711935",
491 | "artistid": 336,
492 | "albumpic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
493 | "originalsongtype": 1,
494 | "songTimeMinutes": "04:16",
495 | "isListenFee": true,
496 | "pic120": "https://img2.kuwo.cn/star/albumcover/120/98/24/205164915.jpg",
497 | "name": "雨下一整晚",
498 | "online": 1,
499 | "payInfo": {
500 | "play": "1111",
501 | "download": "1111",
502 | "local_encrypt": "1",
503 | "limitfree": 0,
504 | "cannotDownload": 0,
505 | "refrain_start": 59084,
506 | "listen_fragment": "1",
507 | "refrain_end": 105386,
508 | "cannotOnlinePlay": 0,
509 | "feeType": {
510 | "song": "1",
511 | "vip": "1"
512 | },
513 | "down": "1111"
514 | }
515 | },
516 | {
517 | "musicrid": "MUSIC_735136",
518 | "barrage": "0",
519 | "artist": "周杰伦",
520 | "mvpayinfo": {
521 | "play": 0,
522 | "vid": 10307550,
523 | "down": 0
524 | },
525 | "pic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
526 | "isstar": 0,
527 | "rid": 735136,
528 | "duration": 170,
529 | "score100": "50",
530 | "content_type": "0",
531 | "track": 7,
532 | "hasLossless": true,
533 | "hasmv": 1,
534 | "releaseDate": "2010-05-18",
535 | "album": "跨时代",
536 | "albumid": 49449,
537 | "pay": "16711935",
538 | "artistid": 336,
539 | "albumpic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
540 | "originalsongtype": 1,
541 | "songTimeMinutes": "02:50",
542 | "isListenFee": true,
543 | "pic120": "https://img2.kuwo.cn/star/albumcover/120/98/24/205164915.jpg",
544 | "name": "嘻哈空姐",
545 | "online": 1,
546 | "payInfo": {
547 | "play": "1111",
548 | "download": "1111",
549 | "local_encrypt": "1",
550 | "limitfree": 0,
551 | "cannotDownload": 0,
552 | "refrain_start": 56367,
553 | "listen_fragment": "1",
554 | "refrain_end": 73699,
555 | "cannotOnlinePlay": 0,
556 | "feeType": {
557 | "song": "1",
558 | "vip": "1"
559 | },
560 | "down": "1111"
561 | }
562 | },
563 | {
564 | "musicrid": "MUSIC_728676",
565 | "barrage": "0",
566 | "artist": "周杰伦",
567 | "mvpayinfo": {
568 | "play": 0,
569 | "vid": 286155,
570 | "down": 0
571 | },
572 | "pic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
573 | "isstar": 0,
574 | "rid": 728676,
575 | "duration": 258,
576 | "score100": "69",
577 | "content_type": "0",
578 | "track": 8,
579 | "hasLossless": true,
580 | "hasmv": 1,
581 | "releaseDate": "2010-05-18",
582 | "album": "跨时代",
583 | "albumid": 49449,
584 | "pay": "16711935",
585 | "artistid": 336,
586 | "albumpic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
587 | "originalsongtype": 1,
588 | "songTimeMinutes": "04:18",
589 | "isListenFee": true,
590 | "pic120": "https://img2.kuwo.cn/star/albumcover/120/98/24/205164915.jpg",
591 | "name": "我落泪情绪零碎",
592 | "online": 1,
593 | "payInfo": {
594 | "play": "1111",
595 | "download": "1111",
596 | "local_encrypt": "1",
597 | "limitfree": 0,
598 | "cannotDownload": 0,
599 | "refrain_start": 72065,
600 | "listen_fragment": "1",
601 | "refrain_end": 134852,
602 | "cannotOnlinePlay": 0,
603 | "feeType": {
604 | "song": "1",
605 | "vip": "1"
606 | },
607 | "down": "1111"
608 | }
609 | },
610 | {
611 | "musicrid": "MUSIC_728668",
612 | "barrage": "0",
613 | "artist": "周杰伦&杨瑞代",
614 | "mvpayinfo": {
615 | "play": 0,
616 | "vid": 7975651,
617 | "down": 0
618 | },
619 | "pic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
620 | "isstar": 0,
621 | "rid": 728668,
622 | "duration": 254,
623 | "score100": "70",
624 | "content_type": "0",
625 | "track": 9,
626 | "hasLossless": true,
627 | "hasmv": 1,
628 | "releaseDate": "2010-05-18",
629 | "album": "跨时代",
630 | "albumid": 49449,
631 | "pay": "16711935",
632 | "artistid": 336,
633 | "albumpic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
634 | "originalsongtype": 1,
635 | "songTimeMinutes": "04:14",
636 | "isListenFee": true,
637 | "pic120": "https://img2.kuwo.cn/star/albumcover/120/98/24/205164915.jpg",
638 | "name": "爱的飞行日记",
639 | "online": 1,
640 | "payInfo": {
641 | "play": "1111",
642 | "download": "1111",
643 | "local_encrypt": "1",
644 | "limitfree": 0,
645 | "cannotDownload": 0,
646 | "refrain_start": 85425,
647 | "listen_fragment": "1",
648 | "refrain_end": 120859,
649 | "cannotOnlinePlay": 0,
650 | "feeType": {
651 | "song": "1",
652 | "vip": "1"
653 | },
654 | "down": "1111"
655 | }
656 | },
657 | {
658 | "musicrid": "MUSIC_728671",
659 | "barrage": "0",
660 | "artist": "周杰伦",
661 | "mvpayinfo": {
662 | "play": 0,
663 | "vid": 10307546,
664 | "down": 0
665 | },
666 | "pic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
667 | "isstar": 0,
668 | "rid": 728671,
669 | "duration": 255,
670 | "score100": "55",
671 | "content_type": "0",
672 | "track": 10,
673 | "hasLossless": true,
674 | "hasmv": 1,
675 | "releaseDate": "2010-05-18",
676 | "album": "跨时代",
677 | "albumid": 49449,
678 | "pay": "16711935",
679 | "artistid": 336,
680 | "albumpic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
681 | "originalsongtype": 1,
682 | "songTimeMinutes": "04:15",
683 | "isListenFee": true,
684 | "pic120": "https://img2.kuwo.cn/star/albumcover/120/98/24/205164915.jpg",
685 | "name": "自导自演",
686 | "online": 1,
687 | "payInfo": {
688 | "play": "1111",
689 | "download": "1111",
690 | "local_encrypt": "1",
691 | "limitfree": 0,
692 | "cannotDownload": 0,
693 | "refrain_start": 69953,
694 | "listen_fragment": "1",
695 | "refrain_end": 99535,
696 | "cannotOnlinePlay": 0,
697 | "feeType": {
698 | "song": "1",
699 | "vip": "1"
700 | },
701 | "down": "1111"
702 | }
703 | },
704 | {
705 | "musicrid": "MUSIC_728672",
706 | "barrage": "0",
707 | "artist": "周杰伦",
708 | "mvpayinfo": {
709 | "play": 0,
710 | "vid": 10307547,
711 | "down": 0
712 | },
713 | "pic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
714 | "isstar": 0,
715 | "rid": 728672,
716 | "duration": 299,
717 | "score100": "62",
718 | "content_type": "0",
719 | "track": 11,
720 | "hasLossless": true,
721 | "hasmv": 1,
722 | "releaseDate": "2010-05-18",
723 | "album": "跨时代",
724 | "albumid": 49449,
725 | "pay": "16711935",
726 | "artistid": 336,
727 | "albumpic": "https://img2.kuwo.cn/star/albumcover/500/98/24/205164915.jpg",
728 | "originalsongtype": 1,
729 | "songTimeMinutes": "04:59",
730 | "isListenFee": true,
731 | "pic120": "https://img2.kuwo.cn/star/albumcover/120/98/24/205164915.jpg",
732 | "name": "超人不会飞",
733 | "online": 1,
734 | "payInfo": {
735 | "play": "1111",
736 | "download": "1111",
737 | "local_encrypt": "1",
738 | "limitfree": 0,
739 | "cannotDownload": 0,
740 | "refrain_start": 99677,
741 | "listen_fragment": "1",
742 | "refrain_end": 146960,
743 | "cannotOnlinePlay": 0,
744 | "feeType": {
745 | "song": "1",
746 | "vip": "1"
747 | },
748 | "down": "1111"
749 | }
750 | }
751 | ]
752 | },
753 | "msg": "success",
754 | "profileId": "site",
755 | "reqId": "f89856d21f50d0b0047c1ae01fb7e86c",
756 | "tId": ""
757 | }
758 | ```
759 |
760 |
761 |
762 | #### - 歌单分类
763 |
764 | 示例:`/kuwo/playList/getTagPlayList?id=2190`
765 |
766 | | 参数 | 说明 | 是否必须 | 示例 |
767 | | ---- | ----------- | -------- | --------- |
768 | | id | 歌单 tag id | 是 | id = 2190 |
769 | | `rn` | 每页条数 | | 默认 30 |
770 | | `pn` | 页数 | | 默认1 |
771 |
772 |
773 |
774 | #### - 歌单分类Tag
775 |
776 | 接口:`/kuwo/getTagList`
777 |
778 | ```json
779 | {
780 | "img": "http://img2.kwcdn.kuwo.cn/star/upload/11/11/1531190823851_.png",
781 | "mdigest": "5",
782 | "data": [
783 | {
784 | "extend": "",
785 | "img": "http://img2.kwcdn.kuwo.cn/star/upload/15/15/1536566688335_.jpg",
786 | "digest": "10000",
787 | "name": "翻唱",
788 | "isnew": "0",
789 | "id": "1848"
790 | },
791 | {
792 | "extend": "",
793 | "img": "http://img4.kwcdn.kuwo.cn/star/upload/13/13/1507883183437_.png",
794 | "digest": "10000",
795 | "name": "网络",
796 | "isnew": "0",
797 | "id": "621"
798 | },
799 | {
800 | "extend": "",
801 | "img": "http://img1.kwcdn.kuwo.cn/star/upload/6/6/1507883159190_.png",
802 | "digest": "10000",
803 | "name": "经典",
804 | "isnew": "0",
805 | "id": "1265"
806 | },
807 | {
808 | "extend": "",
809 | "img": "http://img1.kwcdn.kuwo.cn/star/upload/0/0/1507781397648_.png",
810 | "digest": "10000",
811 | "name": "轻音乐",
812 | "isnew": "0",
813 | "id": "173"
814 | },
815 | {
816 | "extend": "",
817 | "img": "http://img3.kwcdn.kuwo.cn/star/upload/7/7/1517380561751_.png",
818 | "digest": "10000",
819 | "name": "怀旧",
820 | "isnew": "0",
821 | "id": "155"
822 | },
823 | {
824 | "extend": "",
825 | "img": "http://img4.kwcdn.kuwo.cn/star/upload/11/11/1517380562443_.png",
826 | "digest": "10000",
827 | "name": "古风",
828 | "isnew": "0",
829 | "id": "127"
830 | },
831 | {
832 | "extend": "",
833 | "img": "http://img3.kwcdn.kuwo.cn/star/upload/7/7/1517380562103_.png",
834 | "digest": "10000",
835 | "name": "网红",
836 | "isnew": "0",
837 | "id": "1879"
838 | },
839 | {
840 | "extend": "",
841 | "img": "http://img3.kwcdn.kuwo.cn/star/upload/8/8/1507781342248_.png",
842 | "digest": "10000",
843 | "name": "佛乐",
844 | "isnew": "0",
845 | "id": "220"
846 | },
847 | {
848 | "extend": "",
849 | "img": "http://img3.kwcdn.kuwo.cn/star/upload/7/7/1517380562103_.png",
850 | "digest": "10000",
851 | "name": "影视",
852 | "isnew": "0",
853 | "id": "180"
854 | },
855 | {
856 | "extend": "",
857 | "img": "http://img4.kwcdn.kuwo.cn/star/upload/15/15/1507883255871_.png",
858 | "digest": "10000",
859 | "name": "器乐",
860 | "isnew": "0",
861 | "id": "578"
862 | },
863 | {
864 | "extend": "",
865 | "img": "http://img3.kwcdn.kuwo.cn/star/upload/9/9/1517380562105_.png",
866 | "digest": "10000",
867 | "name": "游戏",
868 | "isnew": "0",
869 | "id": "1877"
870 | },
871 | {
872 | "extend": "",
873 | "img": "http://img1.kwcdn.kuwo.cn/star/upload/10/10/1517468038218_.png",
874 | "digest": "10000",
875 | "name": "国漫游戏",
876 | "isnew": "0",
877 | "id": "181"
878 | },
879 | {
880 | "extend": "",
881 | "img": "http://img3.kwcdn.kuwo.cn/star/upload/10/10/1507883200970_.png",
882 | "digest": "10000",
883 | "name": "KTV",
884 | "isnew": "0",
885 | "id": "361"
886 | },
887 | {
888 | "extend": "",
889 | "img": "http://img1.kwcdn.kuwo.cn/star/upload/5/5/1517380561685_.png",
890 | "digest": "10000",
891 | "name": "喊麦",
892 | "isnew": "0",
893 | "id": "216"
894 | },
895 | {
896 | "extend": "|HOT",
897 | "img": "http://img4.kwcdn.kuwo.cn/star/upload/3/3/1536566652003_.jpg",
898 | "digest": "10000",
899 | "name": "抖音",
900 | "isnew": "0",
901 | "id": "2189"
902 | },
903 | {
904 | "extend": "",
905 | "img": "http://kwimg4.kuwo.cn/star/upload/16/73/1551434397397_.jpg",
906 | "digest": "10000",
907 | "name": "3D",
908 | "isnew": "0",
909 | "id": "1366"
910 | },
911 | {
912 | "extend": "",
913 | "img": "http://img2.kwcdn.kuwo.cn/star/upload/15/15/1517380562303_.png",
914 | "digest": "10000",
915 | "name": "店铺专用",
916 | "isnew": "0",
917 | "id": "263"
918 | },
919 | {
920 | "extend": "",
921 | "img": "http://img3.kwcdn.kuwo.cn/star/upload/14/14/1524130384286_.png",
922 | "digest": "10000",
923 | "name": "纯音乐",
924 | "isnew": "0",
925 | "id": "577"
926 | },
927 | {
928 | "extend": "|NEW",
929 | "img": "http://kwimg2.kuwo.cn/star/upload/40/75/1579227646729_.jpg",
930 | "digest": "10000",
931 | "name": "春节",
932 | "isnew": "0",
933 | "id": "2190"
934 | }
935 | ],
936 | "name": "主题",
937 | "id": "5",
938 | "type": "list",
939 | "img1": "http://kwimg2.kuwo.cn/star/upload/46/19/1548987670837_.png"
940 | },
941 | ```
942 |
943 |
944 |
945 |
946 |
947 | ### 歌手
948 |
949 | #### - 全部歌手
950 |
951 | 接口:
952 |
953 | + `/kuwo/singer?category=0&rn=100&pn=1`
954 | + `/kuwo/singer?category=0&rn=100&pn=1&prefix=A`
955 |
956 | 參数: 分类: 0 = 全部 1 = 华语男 2 = 华语女 3 = 华语组合 4 = 日韩男 5 = 日韩女 6 = 日韩组合 7 = 欧美男 8 = 欧美女 9 = 欧美组合 10 = 其他
957 |
958 | | 参数 | 説明 | 是否必須 | 示例 |
959 | | -------- | -------- | -------- | ------- |
960 | | category | 分类 | 是 | 默认 0 |
961 | | `rn` | 每页数量 | | 默认100 |
962 | | `pn` | 分页 | | 默认1 |
963 | | prefix | A~Z 分类 | | |
964 |
965 | #### - 歌手单曲
966 |
967 | 接口:`/kuwo/singer/music?artistid=5371&rn=30&pn=1`
968 |
969 | | 參數 | 説明 | 是否必須 | 示例 |
970 | | ---------- | -------- | -------- | --------------- |
971 | | `artistid` | 歌手id | 是 | `artistid`=5371 |
972 | | `rn` | 每页数量 | | 默认30 |
973 | | `pn` | 分页 | | 默认1 |
974 |
975 | #### - 歌手專輯
976 |
977 | 接口:`/kuwo/singer/album?artistid=5371&rn=30&pn=1`
978 |
979 | | 參數 | 説明 | 是否必須 | 示例 |
980 | | ---------- | -------- | -------- | --------------- |
981 | | `artistid` | 歌手id | 是 | `artistid`=5371 |
982 | | `rn` | 每页数量 | | 默认30 |
983 | | `pn` | 分页 | | 默认1 |
984 |
985 | #### - 歌手mv
986 |
987 | 接口:`/kuwo/singer/mv?artistid=5371&rn=30&pn=1`
988 |
989 | | 參數 | 説明 | 是否必須 | 示例 |
990 | | ---------- | -------- | -------- | --------------- |
991 | | `artistid` | 歌手id | 是 | `artistid`=5371 |
992 | | `rn` | 每页数量 | | 默认30 |
993 | | `pn` | 分页 | | 默认 1 |
994 |
995 | #### - 歌手推荐
996 |
997 | 接口: `/kuwo/rec_singer`
998 |
999 | 示例: `/kuwo/rec_singer?category=11&rn=6&pn=1`
1000 |
1001 | `categroy`: 11 华语 13 欧美 12 日韩 16 组合
1002 |
1003 | | 參數 | 説明 | 是否必須 | 示例 |
1004 | | -------- | -------- | -------- | ------ |
1005 | | category | 分类 | | 默认11 |
1006 | | `rn` | 每页数量 | | 默认6 |
1007 | | `pn` | 页数 | | 默认1 |
1008 |
1009 |
1010 |
1011 | ### 音樂信息
1012 |
1013 | 接口:`/kuwo/musicInfo?mid=162457325`
1014 |
1015 | | 參數 | 説明 | 是否必須 | 示例 |
1016 | | ---- | ------ | -------- | ----------- |
1017 | | mid | 音樂id | 是 | mid=6691107 |
1018 |
1019 |
1020 |
1021 | ### 排行榜
1022 |
1023 | #### - 排行榜單
1024 |
1025 | 接口:`/kuwo/rank`
1026 |
1027 | ```json
1028 | {
1029 | "name": "官方榜",
1030 | "list": [
1031 | {
1032 | "sourceid": "93",
1033 | "intro": "酷我用户每天播放线上歌曲的飙升指数TOP排行榜,为你展示流行趋势、蹿红歌曲,每天更新",
1034 | "name": "酷我飙升榜",
1035 | "id": "489929",
1036 | "source": "2",
1037 | "pic": "http://img3.kwcdn.kuwo.cn/star/upload/4/9/1581375962.png",
1038 | "pub": "今日更新"
1039 | },
1040 | {
1041 | "sourceid": "17",
1042 | "intro": "酷我用户每天播放新歌(一个月内发行)TOP排行榜,为你展示当下潮流新歌,每天更新",
1043 | "name": "酷我新歌榜",
1044 | "id": "489928",
1045 | "source": "2",
1046 | "pic": "http://img3.kwcdn.kuwo.cn/star/upload/6/9/1581203155.png",
1047 | "pub": "今日更新"
1048 | },
1049 | {
1050 | "sourceid": "16",
1051 | "intro": "酷我用户每天播放线上歌曲TOP排行榜,为你展示当下最人气最热门歌曲,每天更新",
1052 | "name": "酷我热歌榜",
1053 | "id": "489927",
1054 | "source": "2",
1055 | "pic": "http://img3.kwcdn.kuwo.cn/star/upload/2/4/1581030357.png",
1056 | "pub": "今日更新"
1057 | },
1058 | {
1059 | "sourceid": "158",
1060 | "intro": "抖音官方热歌TOP排行榜,为你展示最火最洗脑的抖音神曲,每周二更新",
1061 | "name": "抖音热歌榜",
1062 | "id": "490022",
1063 | "source": "2",
1064 | "pic": "http://img3.kwcdn.kuwo.cn/star/upload/4/2/1581375964.png",
1065 | "pub": "今日更新"
1066 | },
1067 | {
1068 | "sourceid": "145",
1069 | "intro": "酷我音乐包歌曲TOP排行榜,为你展示最卖座的高品质无损音乐,每天更新",
1070 | "name": "会员畅听榜",
1071 | "id": "507077",
1072 | "source": "2",
1073 | "pic": "http://img3.kwcdn.kuwo.cn/star/upload/1/6/1580598361.png",
1074 | "pub": "今日更新"
1075 | }
1076 | ]
1077 | }
1078 | ```
1079 |
1080 | #### - 排行榜音樂
1081 |
1082 | 接口:`/kuwo/rank/musicList`
1083 |
1084 | 示例: `/kuwo/rank/musicList?bangId=93&pn=1&rn=30`
1085 |
1086 | | 參數 | 説明 | 是否必須 | 示例 |
1087 | | -------- | -------- | -------- | ----------- |
1088 | | `bangId` | 榜單id | 是 | `bangId`=93 |
1089 | | `pn` | 分頁 | | 默認1 |
1090 | | `rn` | 每頁數量 | | 默認30 |
1091 |
1092 | #### - 推荐榜单
1093 |
1094 | 接口:`/kuwo/rank/rec_bangList`
1095 |
1096 | ```json
1097 | {
1098 | "leader": "酷我热歌榜",
1099 | "num": "350",
1100 | "name": "酷我热歌榜",
1101 | "pic": "http://img1.kwcdn.kuwo.cn/star/upload/12/12/1481783559612_.png",
1102 | "id": "16",
1103 | "pub": "2020-02-11",
1104 | "musicList": [8 items]
1105 | }
1106 | ```
1107 |
1108 |
1109 |
1110 | ### 主播电台
1111 |
1112 | 接口:`/kuwo/radio`
1113 |
1114 | ```json
1115 | Tree
1116 | Chart
1117 | JSON Input
1118 | {
1119 | "code": 200,
1120 | "curTime": 1581407319418,
1121 | "data": {
1122 | "albumList": [
1123 | {
1124 | "artist": "蕊希Erin",
1125 | "album": "蕊希电台",
1126 | "listencnt": "30933",
1127 | "pic": "http://img3.kuwo.cn/star/albumcover/300/52/43/2233596567.jpg",
1128 | "rid": "38721812"
1129 | },
1130 | {
1131 | "artist": "由小藜",
1132 | "album": "非藜不可",
1133 | "listencnt": "145635",
1134 | "pic": "http://img3.kuwo.cn/star/albumcover/300/40/46/3747600081.jpg",
1135 | "rid": "51859818"
1136 | },
1137 | {
1138 | "artist": "睡前一起夜听",
1139 | "album": "睡前一起夜听 ",
1140 | "listencnt": "5537",
1141 | "pic": "http://img3.kuwo.cn/star/albumcover/500/8/74/538223138.jpg",
1142 | "rid": "63807800"
1143 | },
1144 | {
1145 | "artist": "紫云纱",
1146 | "album": "职场情报局",
1147 | "listencnt": "154056",
1148 | "pic": "http://img3.kuwo.cn/star/albumcover/300/56/78/4005148811.jpg",
1149 | "rid": "39277769"
1150 | },
1151 | {
1152 | "artist": "北城[主播]",
1153 | "album": "南城故事",
1154 | "listencnt": "210292",
1155 | "pic": "http://img4.kuwo.cn/star/albumcover/300/47/92/186765640.jpg",
1156 | "rid": "40887125"
1157 | },
1158 | {
1159 | "artist": "中国大百科全书出版社",
1160 | "album": "迷你百科脱口秀第二季",
1161 | "listencnt": "1741",
1162 | "pic": "http://img4.kuwo.cn/star/albumcover/300/79/11/1935561670.jpg",
1163 | "rid": "63722836"
1164 | },
1165 | {
1166 | "artist": "喜剧听我的",
1167 | "album": "小沈阳爆笑作品汇",
1168 | "listencnt": "69499",
1169 | "pic": "http://img3.kuwo.cn/star/albumcover/300/6/0/1039562571.jpg",
1170 | "rid": "68390546"
1171 | },
1172 | {
1173 | "artist": "凯紫",
1174 | "album": "诗词之美",
1175 | "listencnt": "27520",
1176 | "pic": "http://img4.kuwo.cn/star/albumcover/300/2/94/1939976936.jpg",
1177 | "rid": "61002763"
1178 | },
1179 | {
1180 | "artist": "九小如",
1181 | "album": "【美文】听说-你的心情有人懂",
1182 | "listencnt": "376042",
1183 | "pic": "http://img1.kuwo.cn/star/albumcover/300/29/84/1778172532.jpg",
1184 | "rid": "41327034"
1185 | }
1186 | ]
1187 | },
1188 | "msg": "success",
1189 | "profileId": "site",
1190 | "reqId": "cd681719285c24cb5db83494e7fbf0eb"
1191 | }
1192 | ```
1193 |
1194 |
--------------------------------------------------------------------------------
/docs/_coverpage.md:
--------------------------------------------------------------------------------
1 | # 酷我 音乐 Api
2 |
3 | > 酷我音乐 NodeJS 版 API
4 |
5 | [GitHub](https://github.com/QiuYaohong/kuwoMusicApi.git)
6 | [Get Started](#酷我音乐API)
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qyhqiu/kuwoMusicApi/e8e720b90b4d7e3052078a3380906f2b3349e388/docs/favicon.ico
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Document
6 |
7 |
8 |
9 | 酷我音乐 NodeJS 版 API
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kuwomusicapi",
3 | "version": "1.0.0",
4 | "description": "Egg for 酷我音乐API;酷我音乐API Node.js版",
5 | "private": true,
6 | "keywords": [
7 | "酷我音乐API Node.JS版",
8 | "音乐API",
9 | "酷我音乐API"
10 | ],
11 | "egg": {
12 | "typescript": true,
13 | "declarations": true
14 | },
15 | "scripts": {
16 | "start": "npm run ci && egg-scripts start --port=7002 --daemon --title=egg-server-kuwoMusicApi",
17 | "stop": "egg-scripts stop --title=egg-server-kuwoMusicApi",
18 | "docs": "docsify serve ./docs",
19 | "dev": "npm run clean && egg-bin dev --port 7002",
20 | "debug": "egg-bin debug",
21 | "test-local": "egg-bin test",
22 | "test": "npm run lint -- --fix && npm run test-local",
23 | "cov": "egg-bin cov",
24 | "tsc": "ets && tsc -p tsconfig.json",
25 | "ci": "npm run lint && npm run cov && npm run tsc",
26 | "autod": "autod",
27 | "lint": "eslint . --ext .ts",
28 | "clean": "tsc -b --clean"
29 | },
30 | "dependencies": {
31 | "docsify": "^4.13.1",
32 | "egg": "^3.17.3",
33 | "egg-cors": "^2.2.3",
34 | "egg-scripts": "^2.17.0",
35 | "fs-extra": "^11.1.1",
36 | "iconv-lite": "^0.6.3",
37 | "needle": "^3.2.0",
38 | "uuid": "^9.0.0",
39 | "zlib": "^1.0.5"
40 | },
41 | "devDependencies": {
42 | "@eggjs/tsconfig": "^1.3.3",
43 | "@types/mocha": "^10.0.1",
44 | "@types/node": "^20.4.4",
45 | "@types/supertest": "^2.0.12",
46 | "@typescript-eslint/eslint-plugin": "^6.1.0",
47 | "autod": "^3.1.2",
48 | "autod-egg": "^1.1.0",
49 | "docsify-cli": "^4.4.4",
50 | "egg-bin": "^6.4.1",
51 | "egg-ci": "^2.2.0",
52 | "egg-mock": "^5.10.8",
53 | "eslint": "^8.45.0",
54 | "eslint-config-egg": "^12.2.1",
55 | "eslint-config-standard": "^17.1.0",
56 | "eslint-plugin-html": "^7.1.0",
57 | "eslint-plugin-jsdoc": "^46.4.4",
58 | "eslint-plugin-node": "^11.1.0",
59 | "eslint-plugin-promise": "^6.1.1",
60 | "tslib": "^2.6.0",
61 | "typescript": "^5.1.6"
62 | },
63 | "engines": {
64 | "node": ">=8.9.0"
65 | },
66 | "ci": {
67 | "version": "8"
68 | },
69 | "repository": {
70 | "type": "git",
71 | "url": "https://github.com/QiuYaohong/kuwoMusicApi.git"
72 | },
73 | "eslintIgnore": [
74 | "coverage"
75 | ],
76 | "author": "qyhqiu",
77 | "license": "ISC"
78 | }
79 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": true,
3 | "compilerOptions": {
4 | "target": "es2017",
5 | "module": "commonjs",
6 | "strict": true,
7 | "noImplicitAny": false,
8 | "experimentalDecorators": true,
9 | "emitDecoratorMetadata": true,
10 | "allowJs": false,
11 | "pretty": true,
12 | "noEmitOnError": false,
13 | "noUnusedLocals": true,
14 | "noUnusedParameters": true,
15 | "allowUnreachableCode": false,
16 | "allowUnusedLabels": true,
17 | "strictPropertyInitialization": false,
18 | "noFallthroughCasesInSwitch": true,
19 | "skipLibCheck": true,
20 | "skipDefaultLibCheck": true,
21 | "inlineSourceMap": true,
22 | "importHelpers": true
23 | },
24 | "extends": "@eggjs/tsconfig",
25 | "exclude": [
26 | "app/public",
27 | "app/views",
28 | "node_modules*"
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/typings/app/controller/index.d.ts:
--------------------------------------------------------------------------------
1 | // This file is created by egg-ts-helper@1.34.7
2 | // Do not modify this file!!!!!!!!!
3 | /* eslint-disable */
4 |
5 | import 'egg';
6 | import ExportAlbumInfo from '../../../app/controller/albumInfo';
7 | import ExportBanner from '../../../app/controller/banner';
8 | import ExportBaseController from '../../../app/controller/BaseController';
9 | import ExportComment from '../../../app/controller/comment';
10 | import ExportGetTagList from '../../../app/controller/getTagList';
11 | import ExportLrc from '../../../app/controller/lrc';
12 | import ExportMusicInfo from '../../../app/controller/musicInfo';
13 | import ExportMusicList from '../../../app/controller/musicList';
14 | import ExportMv from '../../../app/controller/mv';
15 | import ExportPlayList from '../../../app/controller/playList';
16 | import ExportPlayUrl from '../../../app/controller/playUrl';
17 | import ExportRadio from '../../../app/controller/radio';
18 | import ExportRank from '../../../app/controller/rank';
19 | import ExportRecGedan from '../../../app/controller/recGedan';
20 | import ExportRecSinger from '../../../app/controller/recSinger';
21 | import ExportSearch from '../../../app/controller/search';
22 | import ExportSinger from '../../../app/controller/singer';
23 |
24 | declare module 'egg' {
25 | interface IController {
26 | albumInfo: ExportAlbumInfo;
27 | banner: ExportBanner;
28 | baseController: ExportBaseController;
29 | comment: ExportComment;
30 | getTagList: ExportGetTagList;
31 | lrc: ExportLrc;
32 | musicInfo: ExportMusicInfo;
33 | musicList: ExportMusicList;
34 | mv: ExportMv;
35 | playList: ExportPlayList;
36 | playUrl: ExportPlayUrl;
37 | radio: ExportRadio;
38 | rank: ExportRank;
39 | recGedan: ExportRecGedan;
40 | recSinger: ExportRecSinger;
41 | search: ExportSearch;
42 | singer: ExportSinger;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/typings/app/index.d.ts:
--------------------------------------------------------------------------------
1 | // This file is created by egg-ts-helper@1.34.7
2 | // Do not modify this file!!!!!!!!!
3 | /* eslint-disable */
4 |
5 | import 'egg';
6 | export * from 'egg';
7 | export as namespace Egg;
8 |
--------------------------------------------------------------------------------
/typings/app/service/index.d.ts:
--------------------------------------------------------------------------------
1 | // This file is created by egg-ts-helper@1.34.7
2 | // Do not modify this file!!!!!!!!!
3 | /* eslint-disable */
4 |
5 | import 'egg';
6 | type AnyClass = new (...args: any[]) => any;
7 | type AnyFunc = (...args: any[]) => T;
8 | type CanExportFunc = AnyFunc> | AnyFunc>;
9 | type AutoInstanceType : T> = U extends AnyClass ? InstanceType : U;
10 | import ExportAlbumInfo from '../../../app/service/albumInfo';
11 | import ExportBanner from '../../../app/service/banner';
12 | import ExportBaseService from '../../../app/service/BaseService';
13 | import ExportComment from '../../../app/service/comment';
14 | import ExportGetTagList from '../../../app/service/getTagList';
15 | import ExportLrc from '../../../app/service/lrc';
16 | import ExportMusicInfo from '../../../app/service/musicInfo';
17 | import ExportMusicList from '../../../app/service/musicList';
18 | import ExportMv from '../../../app/service/mv';
19 | import ExportPlayList from '../../../app/service/playList';
20 | import ExportPlayUrl from '../../../app/service/playUrl';
21 | import ExportRadio from '../../../app/service/radio';
22 | import ExportRank from '../../../app/service/rank';
23 | import ExportRecGedan from '../../../app/service/recGedan';
24 | import ExportRecSinger from '../../../app/service/recSinger';
25 | import ExportSearch from '../../../app/service/search';
26 | import ExportSinger from '../../../app/service/singer';
27 |
28 | declare module 'egg' {
29 | interface IService {
30 | albumInfo: AutoInstanceType;
31 | banner: AutoInstanceType;
32 | baseService: AutoInstanceType;
33 | comment: AutoInstanceType;
34 | getTagList: AutoInstanceType;
35 | lrc: AutoInstanceType;
36 | musicInfo: AutoInstanceType;
37 | musicList: AutoInstanceType;
38 | mv: AutoInstanceType;
39 | playList: AutoInstanceType;
40 | playUrl: AutoInstanceType;
41 | radio: AutoInstanceType;
42 | rank: AutoInstanceType;
43 | recGedan: AutoInstanceType;
44 | recSinger: AutoInstanceType;
45 | search: AutoInstanceType;
46 | singer: AutoInstanceType;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/typings/config/index.d.ts:
--------------------------------------------------------------------------------
1 | // This file is created by egg-ts-helper@1.34.7
2 | // Do not modify this file!!!!!!!!!
3 | /* eslint-disable */
4 |
5 | import 'egg';
6 | import { EggAppConfig } from 'egg';
7 | import ExportConfigDefault from '../../config/config.default';
8 | type ConfigDefault = ReturnType;
9 | type NewEggAppConfig = ConfigDefault;
10 | declare module 'egg' {
11 | interface EggAppConfig extends NewEggAppConfig { }
12 | }
--------------------------------------------------------------------------------
/typings/config/plugin.d.ts:
--------------------------------------------------------------------------------
1 | // This file is created by egg-ts-helper@1.34.7
2 | // Do not modify this file!!!!!!!!!
3 | /* eslint-disable */
4 |
5 | import 'egg';
6 | import 'egg-onerror';
7 | import 'egg-session';
8 | import 'egg-i18n';
9 | import 'egg-watcher';
10 | import 'egg-multipart';
11 | import 'egg-security';
12 | import 'egg-development';
13 | import 'egg-logrotator';
14 | import 'egg-schedule';
15 | import 'egg-static';
16 | import 'egg-jsonp';
17 | import 'egg-view';
18 | import 'egg-cors';
19 | import { EggPluginItem } from 'egg';
20 | declare module 'egg' {
21 | interface EggPlugin {
22 | onerror?: EggPluginItem;
23 | session?: EggPluginItem;
24 | i18n?: EggPluginItem;
25 | watcher?: EggPluginItem;
26 | multipart?: EggPluginItem;
27 | security?: EggPluginItem;
28 | development?: EggPluginItem;
29 | logrotator?: EggPluginItem;
30 | schedule?: EggPluginItem;
31 | static?: EggPluginItem;
32 | jsonp?: EggPluginItem;
33 | view?: EggPluginItem;
34 | cors?: EggPluginItem;
35 | }
36 | }
--------------------------------------------------------------------------------
/typings/index.d.ts:
--------------------------------------------------------------------------------
1 | import 'egg';
2 |
3 | declare module 'egg' {
4 |
5 | }
--------------------------------------------------------------------------------