@sansenjian/qq-music-api 2.2.9 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/CHANGELOG.md +28 -1
  2. package/README.md +10 -11
  3. package/dist/api/index.js +9 -0
  4. package/dist/app.js +8 -43
  5. package/dist/index.js +13 -1
  6. package/dist/koaApp.js +49 -0
  7. package/dist/module/apis/downloadQQMusic.js +2 -1
  8. package/dist/module/apis/music/getLyric.js +220 -16
  9. package/dist/module/apis/music/getMusicPlay.js +3 -2
  10. package/dist/package.json +19 -15
  11. package/dist/routers/context/getLyric.js +12 -2
  12. package/dist/routers/context/getNewDisks.js +4 -0
  13. package/dist/routers/context/getRecommend.js +4 -0
  14. package/dist/routers/context/getTicketInfo.js +4 -0
  15. package/dist/util/cookieResolver.js +10 -4
  16. package/docs-dist/404.html +6 -6
  17. package/docs-dist/CHANGELOG-ARCHITECTURE.html +14 -14
  18. package/docs-dist/COOKIE_CONFIG_GUIDE.html +13 -13
  19. package/docs-dist/DEGRADE_EXAMPLES.html +109 -0
  20. package/docs-dist/FALLBACK_MODE_GUIDE.html +21 -21
  21. package/docs-dist/QUICK_START.html +62 -0
  22. package/docs-dist/README.html +17 -17
  23. package/docs-dist/TEST_USER_PLAYLISTS.html +14 -14
  24. package/docs-dist/USER_AVATAR_GUIDE.html +16 -16
  25. package/docs-dist/api/comments.html +12 -12
  26. package/docs-dist/api/index.html +10 -10
  27. package/docs-dist/api/music.html +15 -13
  28. package/docs-dist/api/other.html +13 -13
  29. package/docs-dist/api/playground.html +27 -0
  30. package/docs-dist/api/playlist.html +15 -15
  31. package/docs-dist/api/rank.html +12 -12
  32. package/docs-dist/api/search.html +13 -13
  33. package/docs-dist/api/singer.html +12 -12
  34. package/docs-dist/api/user.html +16 -16
  35. package/docs-dist/assets/{CHANGELOG-ARCHITECTURE.md.r40JGJZK.js → CHANGELOG-ARCHITECTURE.md.CYHmaBdY.js} +5 -5
  36. package/docs-dist/assets/CHANGELOG-ARCHITECTURE.md.CYHmaBdY.lean.js +1 -0
  37. package/docs-dist/assets/COOKIE_CONFIG_GUIDE.md.Nvywo7CW.js +13 -0
  38. package/docs-dist/assets/COOKIE_CONFIG_GUIDE.md.Nvywo7CW.lean.js +1 -0
  39. package/docs-dist/assets/DEGRADE_EXAMPLES.md.Cz2J-qwE.js +83 -0
  40. package/docs-dist/assets/DEGRADE_EXAMPLES.md.Cz2J-qwE.lean.js +1 -0
  41. package/docs-dist/assets/{FALLBACK_MODE_GUIDE.md.BBdcIdh_.js → FALLBACK_MODE_GUIDE.md.wKA9yqoI.js} +12 -12
  42. package/docs-dist/assets/FALLBACK_MODE_GUIDE.md.wKA9yqoI.lean.js +1 -0
  43. package/docs-dist/assets/QUICK_START.md.D5KfDgbs.js +36 -0
  44. package/docs-dist/assets/QUICK_START.md.D5KfDgbs.lean.js +1 -0
  45. package/docs-dist/assets/README.md.CHbArqA7.js +421 -0
  46. package/docs-dist/assets/README.md.CHbArqA7.lean.js +1 -0
  47. package/docs-dist/assets/TEST_USER_PLAYLISTS.md.BfBxYbaM.js +16 -0
  48. package/docs-dist/assets/TEST_USER_PLAYLISTS.md.BfBxYbaM.lean.js +1 -0
  49. package/docs-dist/assets/{USER_AVATAR_GUIDE.md.CVHPs2Dn.js → USER_AVATAR_GUIDE.md.D5Rti1Gg.js} +7 -7
  50. package/docs-dist/assets/USER_AVATAR_GUIDE.md.D5Rti1Gg.lean.js +1 -0
  51. package/docs-dist/assets/{api_comments.md.79Q_C8Qp.js → api_comments.md.5nDhrWa8.js} +3 -3
  52. package/docs-dist/assets/api_comments.md.5nDhrWa8.lean.js +1 -0
  53. package/docs-dist/assets/api_index.md.DdG1WHkZ.js +1 -0
  54. package/docs-dist/assets/api_index.md.DdG1WHkZ.lean.js +1 -0
  55. package/docs-dist/assets/{api_music.md.B1AzLePX.js → api_music.md.D66hq-_4.js} +6 -4
  56. package/docs-dist/assets/api_music.md.D66hq-_4.lean.js +1 -0
  57. package/docs-dist/assets/api_other.md.9KhspVEM.js +7 -0
  58. package/docs-dist/assets/api_other.md.9KhspVEM.lean.js +1 -0
  59. package/docs-dist/assets/api_playground.md.BZBvYMm2.js +11 -0
  60. package/docs-dist/assets/api_playground.md.BZBvYMm2.lean.js +11 -0
  61. package/docs-dist/assets/{api_playlist.md.8ACJ3QqD.js → api_playlist.md.BQK32ZyE.js} +6 -6
  62. package/docs-dist/assets/api_playlist.md.BQK32ZyE.lean.js +1 -0
  63. package/docs-dist/assets/{api_rank.md.B8IP2ZRy.js → api_rank.md.z7YBwVgw.js} +3 -3
  64. package/docs-dist/assets/api_rank.md.z7YBwVgw.lean.js +1 -0
  65. package/docs-dist/assets/{api_search.md.DO9J6nvp.js → api_search.md.GfzIBRfc.js} +4 -4
  66. package/docs-dist/assets/api_search.md.GfzIBRfc.lean.js +1 -0
  67. package/docs-dist/assets/api_singer.md.BDR-_qDH.js +21 -0
  68. package/docs-dist/assets/api_singer.md.BDR-_qDH.lean.js +1 -0
  69. package/docs-dist/assets/{api_user.md.Cb7Ky3Sn.js → api_user.md.Dx8BWGrb.js} +7 -7
  70. package/docs-dist/assets/api_user.md.Dx8BWGrb.lean.js +1 -0
  71. package/docs-dist/assets/app.CxuIZ1W7.js +1 -0
  72. package/docs-dist/assets/chunks/@localSearchIndexroot.ygoKgu27.js +1 -0
  73. package/docs-dist/assets/chunks/VPLocalSearchBox.BqgkAhNM.js +3 -0
  74. package/docs-dist/assets/chunks/framework.BUY3a635.js +4 -0
  75. package/docs-dist/assets/chunks/theme.C-Z3DN0r.js +2 -0
  76. package/docs-dist/assets/{guide_architecture.md.CzgqynmB.js → guide_architecture.md.SHnKkzwb.js} +20 -20
  77. package/docs-dist/assets/guide_architecture.md.SHnKkzwb.lean.js +1 -0
  78. package/docs-dist/assets/guide_authentication.md.CZCKocgR.js +4 -0
  79. package/docs-dist/assets/guide_authentication.md.CZCKocgR.lean.js +1 -0
  80. package/docs-dist/assets/guide_index.md.CkJ-jjL0.js +1 -0
  81. package/docs-dist/assets/guide_index.md.CkJ-jjL0.lean.js +1 -0
  82. package/docs-dist/assets/guide_installation.md.D2TBzILh.js +7 -0
  83. package/docs-dist/assets/guide_installation.md.D2TBzILh.lean.js +1 -0
  84. package/docs-dist/assets/guide_quickstart.md.J7Sib8wg.js +13 -0
  85. package/docs-dist/assets/guide_quickstart.md.J7Sib8wg.lean.js +1 -0
  86. package/docs-dist/assets/index.md.ClwYf6Qc.js +1 -0
  87. package/docs-dist/assets/index.md.ClwYf6Qc.lean.js +1 -0
  88. package/docs-dist/assets/inter-italic-cyrillic-ext._dlW9xFb.woff2 +0 -0
  89. package/docs-dist/assets/inter-italic-cyrillic.D7dRslh9.woff2 +0 -0
  90. package/docs-dist/assets/inter-italic-greek-ext.Ct-Tf2bq.woff2 +0 -0
  91. package/docs-dist/assets/inter-italic-greek.DNcpQ8QC.woff2 +0 -0
  92. package/docs-dist/assets/inter-italic-latin-ext.DytegdRQ.woff2 +0 -0
  93. package/docs-dist/assets/inter-italic-latin.COaG5lWR.woff2 +0 -0
  94. package/docs-dist/assets/inter-italic-vietnamese.BI5UxJD-.woff2 +0 -0
  95. package/docs-dist/assets/inter-roman-cyrillic-ext.BeNbU08G.woff2 +0 -0
  96. package/docs-dist/assets/inter-roman-cyrillic.CD0kT8R4.woff2 +0 -0
  97. package/docs-dist/assets/inter-roman-greek-ext.CFAEQ5Ow.woff2 +0 -0
  98. package/docs-dist/assets/inter-roman-greek.Dsf7YjP7.woff2 +0 -0
  99. package/docs-dist/assets/inter-roman-latin-ext.Dl_ayf4-.woff2 +0 -0
  100. package/docs-dist/assets/inter-roman-latin.Cy4MYw_J.woff2 +0 -0
  101. package/docs-dist/assets/inter-roman-vietnamese.CpqCnS2H.woff2 +0 -0
  102. package/docs-dist/assets/reference_response-format.md.BrGoGoPV.js +12 -0
  103. package/docs-dist/assets/reference_response-format.md.BrGoGoPV.lean.js +1 -0
  104. package/docs-dist/assets/style.D_YoXH3a.css +1 -0
  105. package/docs-dist/guide/architecture.html +29 -29
  106. package/docs-dist/guide/authentication.html +12 -12
  107. package/docs-dist/guide/index.html +10 -10
  108. package/docs-dist/guide/installation.html +13 -13
  109. package/docs-dist/guide/quickstart.html +15 -15
  110. package/docs-dist/hashmap.json +1 -1
  111. package/docs-dist/index.html +10 -10
  112. package/docs-dist/reference/response-format.html +13 -13
  113. package/docs-dist/version.json +3 -3
  114. package/package.json +19 -15
  115. package/public/index.html +966 -0
  116. package/public/playground-utils.js +150 -0
  117. package/dist/jest.config.js +0 -52
  118. package/dist/scripts/run-tests-with-flags.js +0 -139
  119. package/dist/types/api.js +0 -55
  120. package/docs-dist/assets/CHANGELOG-ARCHITECTURE.md.r40JGJZK.lean.js +0 -1
  121. package/docs-dist/assets/COOKIE_CONFIG_GUIDE.md.BVXl7WHu.js +0 -13
  122. package/docs-dist/assets/COOKIE_CONFIG_GUIDE.md.BVXl7WHu.lean.js +0 -1
  123. package/docs-dist/assets/FALLBACK_MODE_GUIDE.md.BBdcIdh_.lean.js +0 -1
  124. package/docs-dist/assets/README.md.D6Tw0nRd.js +0 -421
  125. package/docs-dist/assets/README.md.D6Tw0nRd.lean.js +0 -1
  126. package/docs-dist/assets/TEST_USER_PLAYLISTS.md.DSt20Igj.js +0 -16
  127. package/docs-dist/assets/TEST_USER_PLAYLISTS.md.DSt20Igj.lean.js +0 -1
  128. package/docs-dist/assets/USER_AVATAR_GUIDE.md.CVHPs2Dn.lean.js +0 -1
  129. package/docs-dist/assets/api_comments.md.79Q_C8Qp.lean.js +0 -1
  130. package/docs-dist/assets/api_index.md.CU3By8tw.js +0 -1
  131. package/docs-dist/assets/api_index.md.CU3By8tw.lean.js +0 -1
  132. package/docs-dist/assets/api_music.md.B1AzLePX.lean.js +0 -1
  133. package/docs-dist/assets/api_other.md.DCg4bzA7.js +0 -7
  134. package/docs-dist/assets/api_other.md.DCg4bzA7.lean.js +0 -1
  135. package/docs-dist/assets/api_playlist.md.8ACJ3QqD.lean.js +0 -1
  136. package/docs-dist/assets/api_rank.md.B8IP2ZRy.lean.js +0 -1
  137. package/docs-dist/assets/api_search.md.DO9J6nvp.lean.js +0 -1
  138. package/docs-dist/assets/api_singer.md.CcL32xuN.js +0 -21
  139. package/docs-dist/assets/api_singer.md.CcL32xuN.lean.js +0 -1
  140. package/docs-dist/assets/api_user.md.Cb7Ky3Sn.lean.js +0 -1
  141. package/docs-dist/assets/app.CSainqD9.js +0 -1
  142. package/docs-dist/assets/chunks/@localSearchIndexroot.BKleDIv-.js +0 -1
  143. package/docs-dist/assets/chunks/VPLocalSearchBox.BUBaq7tw.js +0 -9
  144. package/docs-dist/assets/chunks/framework.aJbMEiY9.js +0 -19
  145. package/docs-dist/assets/chunks/theme.CzMhU0Ps.js +0 -2
  146. package/docs-dist/assets/guide_architecture.md.CzgqynmB.lean.js +0 -1
  147. package/docs-dist/assets/guide_authentication.md.a8yTA8Xe.js +0 -4
  148. package/docs-dist/assets/guide_authentication.md.a8yTA8Xe.lean.js +0 -1
  149. package/docs-dist/assets/guide_index.md.BgUUL6fI.js +0 -1
  150. package/docs-dist/assets/guide_index.md.BgUUL6fI.lean.js +0 -1
  151. package/docs-dist/assets/guide_installation.md.BCZ4jBl_.js +0 -7
  152. package/docs-dist/assets/guide_installation.md.BCZ4jBl_.lean.js +0 -1
  153. package/docs-dist/assets/guide_quickstart.md.9-4dA6wS.js +0 -13
  154. package/docs-dist/assets/guide_quickstart.md.9-4dA6wS.lean.js +0 -1
  155. package/docs-dist/assets/index.md.z0hAJioN.js +0 -1
  156. package/docs-dist/assets/index.md.z0hAJioN.lean.js +0 -1
  157. package/docs-dist/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
  158. package/docs-dist/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
  159. package/docs-dist/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
  160. package/docs-dist/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
  161. package/docs-dist/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
  162. package/docs-dist/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
  163. package/docs-dist/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
  164. package/docs-dist/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
  165. package/docs-dist/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
  166. package/docs-dist/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
  167. package/docs-dist/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
  168. package/docs-dist/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
  169. package/docs-dist/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
  170. package/docs-dist/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
  171. package/docs-dist/assets/reference_response-format.md.VvQTLDZr.js +0 -12
  172. package/docs-dist/assets/reference_response-format.md.VvQTLDZr.lean.js +0 -1
  173. package/docs-dist/assets/style.DM4qKDd4.css +0 -1
package/CHANGELOG.md CHANGED
@@ -1,4 +1,31 @@
1
- ## 2.2.9 (2026-03-14)
1
+ ## 2.3.1 (2026-05-17)
2
+
3
+
4
+
5
+ ## 2.3.1 (2026-05-17)
6
+
7
+
8
+ ### Features
9
+
10
+ * refresh docs and package contents ([#15](https://github.com/sansenjian/qq-music-api/issues/15)) ([2b8cca1](https://github.com/sansenjian/qq-music-api/commit/2b8cca11ef9eff1daca4bbc5c3c5783124434471))
11
+
12
+
13
+
14
+ ## 2.2.11 (2026-03-21)
15
+
16
+
17
+ ### Bug Fixes
18
+
19
+ * Dockerfile to reduce vulnerabilities ([#14](https://github.com/sansenjian/qq-music-api/issues/14)) ([a1e439d](https://github.com/sansenjian/qq-music-api/commit/a1e439d25bd5f19c93aeb4f67a6f26ba660248d0))
20
+
21
+
22
+
23
+ ## 2.2.10 (2026-03-14)
24
+
25
+
26
+ ### Bug Fixes
27
+
28
+ * stabilize lyric flow and refresh CI/Vercel config ([#13](https://github.com/sansenjian/qq-music-api/issues/13)) ([34ed7b1](https://github.com/sansenjian/qq-music-api/commit/34ed7b164fa74e12d753867b5a7b49d3b5bf0200))
2
29
 
3
30
 
4
31
 
package/README.md CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  <div align="center">
4
4
 
5
- <img src='music.png' />
6
-
7
5
  ![GitHub watchers](https://img.shields.io/github/watchers/sansenjian/qq-music-api?style=social) ![GitHub stars](https://img.shields.io/github/stars/sansenjian/qq-music-api?style=social) ![GitHub forks](https://img.shields.io/github/forks/sansenjian/qq-music-api?style=social)
8
6
  <br />
9
7
  ![node](https://img.shields.io/badge/node-%3E%3D20.0.0-green?style=flat-square)
@@ -18,13 +16,15 @@
18
16
  > 基于 Koa 2 与 TypeScript 的 QQ 音乐 API 服务,包含扫码登录、用户头像、MV 播放地址、批量接口等能力。
19
17
  > 当前代码仅供学习与研究使用,不可做商业用途。
20
18
 
19
+ > 从 `2.3.0` 开始,新版本会持续减少 npm 包安装时的依赖体积,推荐新项目和升级用户优先使用 `2.3` 及之后版本。
20
+
21
21
  ## 项目概览
22
22
 
23
23
  - 运行时:Node.js 20+
24
24
  - 服务框架:Koa 2
25
25
  - 开发语言:TypeScript
26
26
  - 路由系统:[@koa/router](package.json:53)
27
- - 文档系统:[`VitePress`](package.json:100)
27
+ - 文档系统:[VitePress 2](https://vitepress.dev/)
28
28
  - 测试框架:[`Jest`](package.json:89)
29
29
  - 默认端口:`3200`
30
30
 
@@ -32,7 +32,6 @@
32
32
 
33
33
  > 当前版本已包含扫码登录相关接口,并已完成 TypeScript 迁移。
34
34
 
35
- ![qq-music](./screenshot/qq-music.png)
36
35
 
37
36
  📖 **详细 API 文档**: [查看完整 API 文档](https://sansenjian.github.io/qq-music-api/)
38
37
 
@@ -60,13 +59,15 @@ npm install
60
59
  npm install @sansenjian/qq-music-api
61
60
  ```
62
61
 
62
+ NPM 包默认包含运行所需的 `dist/`、本地交互测试页 `public/`,以及已构建的文档站点 `docs-dist/`。如果你只想在自己的发布产物中保留 API 运行时,可以在你的项目打包配置中自行排除 `node_modules/@sansenjian/qq-music-api/docs-dist/` 或 `node_modules/@sansenjian/qq-music-api/public/`。
63
+
63
64
  在项目中使用:
64
65
 
65
66
  ```javascript
66
67
  const { spawn } = require('child_process');
67
68
  const path = require('path');
68
69
 
69
- const qqMusicPath = path.join(__dirname, 'node_modules', '@sansenjian/qq-music-api', 'app.js');
70
+ const qqMusicPath = path.join(__dirname, 'node_modules', '@sansenjian/qq-music-api', 'dist', 'app.js');
70
71
 
71
72
  spawn('node', [qqMusicPath], {
72
73
  env: { ...process.env, PORT: '3200' },
@@ -102,13 +103,11 @@ npm run docs:dev
102
103
  | 依赖 | 当前版本 |
103
104
  | ----------------------- | --------- |
104
105
  | `@koa/router` | `^15.3.1` |
105
- | `axios` | `^1.13.6` |
106
- | `date-fns` | `^4.1.0` |
107
- | `is-generator-function` | `1.0.10` |
106
+ | `axios` | `^1.16.0` |
107
+ | `chalk` | `^4.1.0` |
108
108
  | `koa` | `^2.16.1` |
109
109
  | `koa-bodyparser` | `^4.4.1` |
110
110
  | `koa-static` | `^5.0.0` |
111
- | `reflect-metadata` | `^0.2.2` |
112
111
 
113
112
  ### 开发依赖
114
113
 
@@ -127,7 +126,6 @@ npm run docs:dev
127
126
  | `@types/koa-bodyparser` | `^4.3.13` |
128
127
  | `@types/node` | `^25.3.3` |
129
128
  | `@types/supertest` | `^7.2.0` |
130
- | `chalk` | `^4.1.0` |
131
129
  | `conventional-changelog-cli` | `^4.0.0` |
132
130
  | `eslint` | `^8.57.0` |
133
131
  | `eslint-config-standard` | `^17.0.0` |
@@ -146,7 +144,7 @@ npm run docs:dev
146
144
  | `ts-node` | `^10.9.2` |
147
145
  | `tsx` | `^4.21.0` |
148
146
  | `typescript` | `5.7.3` |
149
- | `vitepress` | `^1.6.4` |
147
+ | `vitepress` | `^2.0.0-alpha.17` |
150
148
  | `vue` | `^3.5.29` |
151
149
 
152
150
  ## 当前主要能力
@@ -185,6 +183,7 @@ npm run docs:dev
185
183
 
186
184
  - 用户接口说明:[`docs/api/user.md`](docs/api/user.md)
187
185
  - 其他接口说明:[`docs/api/other.md`](docs/api/other.md)
186
+ - API 调试台:[`docs/api/playground.md`](docs/api/playground.md)
188
187
  - 用户歌单测试说明:[`docs/TEST_USER_PLAYLISTS.md`](docs/TEST_USER_PLAYLISTS.md)
189
188
  - 在线文档主页: [https://sansenjian.github.io/qq-music-api/](https://sansenjian.github.io/qq-music-api/)
190
189
 
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const koaApp_1 = __importDefault(require("../koaApp"));
7
+ const handler = koaApp_1.default.callback();
8
+ exports.default = handler;
9
+ module.exports = handler;
package/dist/app.js CHANGED
@@ -3,22 +3,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- require("reflect-metadata");
7
- const koa_1 = __importDefault(require("koa"));
8
- const koa_bodyparser_1 = __importDefault(require("koa-bodyparser"));
9
- const path_1 = __importDefault(require("path"));
10
- const koa_static_1 = __importDefault(require("koa-static"));
11
6
  const chalk_1 = __importDefault(require("chalk"));
12
- const koa_cors_1 = __importDefault(require("./middlewares/koa-cors"));
13
- const router_1 = __importDefault(require("./routers/router"));
14
- const cookie_1 = __importDefault(require("./util/cookie"));
15
- const fallback_middleware_1 = __importDefault(require("./middlewares/fallback-middleware"));
16
7
  const colors_1 = __importDefault(require("./util/colors"));
17
- const user_info_1 = __importDefault(require("./config/user-info"));
18
8
  const service_config_1 = __importDefault(require("./config/service-config"));
19
9
  const package_json_1 = __importDefault(require("./package.json"));
20
- const app = new koa_1.default();
21
- global.userInfo = user_info_1.default;
10
+ const koaApp_1 = __importDefault(require("./koaApp"));
22
11
  // 输出服务配置信息
23
12
  console.log(chalk_1.default.green('\n🎵 QQ Music API Service Starting...\n'));
24
13
  console.log(colors_1.default.info(`Current Version: ${package_json_1.default.version}`));
@@ -43,35 +32,11 @@ if (service_config_1.default.useGlobalCookie) {
43
32
  console.log(chalk_1.default.yellow(`😔 The configuration ${chalk_1.default.red('cookie')} in file ${chalk_1.default.green('config/user-info')} has not configured. \n`));
44
33
  }
45
34
  }
46
- app.use((0, koa_bodyparser_1.default)());
47
- app.use((0, fallback_middleware_1.default)());
48
- app.use((0, cookie_1.default)());
49
- app.use((0, koa_static_1.default)(path_1.default.join(__dirname, 'public')));
50
- // logger
51
- app.use(async (ctx, next) => {
52
- await next();
53
- const rt = ctx.response.get('X-Response-Time');
54
- console.log(colors_1.default.prompt(`${ctx.method} ${ctx.url} - ${rt}`));
55
- });
56
- // cors
57
- app.use((0, koa_cors_1.default)({
58
- origin: ctx => ctx.request.header?.origin || '*',
59
- exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'],
60
- maxAge: 5,
61
- credentials: true,
62
- allowMethods: ['GET', 'POST', 'DELETE'],
63
- allowHeaders: ['Content-Type', 'Authorization', 'Accept'],
64
- }));
65
- // x-response-time
66
- app.use(async (ctx, next) => {
67
- const start = Date.now();
68
- await next();
69
- const ms = Date.now() - start;
70
- ctx.set('X-Response-Time', `${ms}ms`);
71
- });
72
- app.use(router_1.default.routes())
73
- .use(router_1.default.allowedMethods());
74
35
  const PORT = typeof process.env.PORT === 'string' ? parseInt(process.env.PORT, 10) : (process.env.PORT || 3200);
75
- app.listen(PORT, () => {
76
- console.log(colors_1.default.prompt(`server running @ http://localhost:${PORT}`));
77
- });
36
+ const isMainModule = require.main === module;
37
+ if (isMainModule) {
38
+ koaApp_1.default.listen(PORT, () => {
39
+ console.log(colors_1.default.prompt(`server running @ http://localhost:${PORT}`));
40
+ });
41
+ }
42
+ exports.default = koaApp_1.default;
package/dist/index.js CHANGED
@@ -1,3 +1,15 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- require("./app");
6
+ const app_1 = __importDefault(require("./app"));
7
+ const colors_1 = __importDefault(require("./util/colors"));
8
+ const parsedPort = Number.parseInt(process.env.PORT ?? '', 10);
9
+ const PORT = Number.isFinite(parsedPort) ? parsedPort : 3200;
10
+ if (require.main === module) {
11
+ app_1.default.listen(PORT, () => {
12
+ console.log(colors_1.default.prompt(`server running @ http://localhost:${PORT}`));
13
+ });
14
+ }
15
+ exports.default = app_1.default;
package/dist/koaApp.js ADDED
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const koa_1 = __importDefault(require("koa"));
7
+ const koa_bodyparser_1 = __importDefault(require("koa-bodyparser"));
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const koa_static_1 = __importDefault(require("koa-static"));
11
+ const koa_cors_1 = __importDefault(require("./middlewares/koa-cors"));
12
+ const router_1 = __importDefault(require("./routers/router"));
13
+ const cookie_1 = __importDefault(require("./util/cookie"));
14
+ const fallback_middleware_1 = __importDefault(require("./middlewares/fallback-middleware"));
15
+ const colors_1 = __importDefault(require("./util/colors"));
16
+ const user_info_1 = __importDefault(require("./config/user-info"));
17
+ const app = new koa_1.default();
18
+ const publicDir = fs_1.default.existsSync(path_1.default.join(__dirname, 'public'))
19
+ ? path_1.default.join(__dirname, 'public')
20
+ : path_1.default.join(__dirname, '..', 'public');
21
+ global.userInfo = user_info_1.default;
22
+ app.use((0, koa_bodyparser_1.default)());
23
+ app.use((0, fallback_middleware_1.default)());
24
+ app.use((0, cookie_1.default)());
25
+ app.use((0, koa_static_1.default)(publicDir));
26
+ // logger
27
+ app.use(async (ctx, next) => {
28
+ await next();
29
+ const rt = ctx.response.get('X-Response-Time');
30
+ console.log(colors_1.default.prompt(`${ctx.method} ${ctx.url} - ${rt}`));
31
+ });
32
+ // cors
33
+ app.use((0, koa_cors_1.default)({
34
+ origin: ctx => ctx.request.header?.origin || '*',
35
+ exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'],
36
+ maxAge: 5,
37
+ credentials: true,
38
+ allowMethods: ['GET', 'POST', 'DELETE'],
39
+ allowHeaders: ['Content-Type', 'Authorization', 'Accept'],
40
+ }));
41
+ // x-response-time
42
+ app.use(async (ctx, next) => {
43
+ const start = Date.now();
44
+ await next();
45
+ const ms = Date.now() - start;
46
+ ctx.set('X-Response-Time', `${ms}ms`);
47
+ });
48
+ app.use(router_1.default.routes()).use(router_1.default.allowedMethods());
49
+ exports.default = app;
@@ -40,8 +40,9 @@ exports.default = ({ method = 'get', params = {}, option = {} }) => {
40
40
  .catch(error => {
41
41
  console.log('error', error);
42
42
  return {
43
+ status: 502,
43
44
  body: {
44
- error
45
+ error: error instanceof Error ? error.message : error
45
46
  }
46
47
  };
47
48
  });
@@ -4,29 +4,233 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const lyricParse_1 = require("../../../util/lyricParse");
7
- const request_1 = __importDefault(require("../../../util/request"));
8
- const apiResponse_1 = require("../../../util/apiResponse");
7
+ const y_common_1 = __importDefault(require("../y_common"));
8
+ const cookieResolver_1 = require("../../../util/cookieResolver");
9
+ const UCommon_1 = __importDefault(require("../UCommon/UCommon"));
10
+ const MUSICU_TIMEOUT_MS = 10000;
11
+ const getCookieFromOptions = (option) => {
12
+ if (!option || typeof option !== 'object') {
13
+ return undefined;
14
+ }
15
+ const headers = option.headers;
16
+ if (!headers || typeof headers !== 'object') {
17
+ return undefined;
18
+ }
19
+ const cookie = headers.Cookie || headers.cookie;
20
+ return typeof cookie === 'string' ? cookie : undefined;
21
+ };
22
+ const resolveUin = (cookie) => {
23
+ return (0, cookieResolver_1.extractUinFromCookie)(cookie) || String(global.userInfo?.loginUin || '0');
24
+ };
25
+ const decodeLyricField = (value) => {
26
+ if (typeof value !== 'string' || !value) {
27
+ return '';
28
+ }
29
+ try {
30
+ const decoded = Buffer.from(value, 'base64').toString();
31
+ return decoded || value;
32
+ }
33
+ catch {
34
+ return value;
35
+ }
36
+ };
37
+ const normalizeLyricResponse = (resData, isFormat) => {
38
+ const lyricString = decodeLyricField(resData?.lyric);
39
+ const lyric = isFormat && lyricString ? (0, lyricParse_1.lyricParse)(lyricString) : lyricString;
40
+ return {
41
+ ...resData,
42
+ lyric
43
+ };
44
+ };
45
+ const hasNegativeBizCode = (data) => {
46
+ const codes = [data?.retcode, data?.code, data?.subcode]
47
+ .map(item => Number(item))
48
+ .filter(item => !Number.isNaN(item));
49
+ return codes.some(item => item < 0);
50
+ };
51
+ const normalizeSongId = (value) => {
52
+ const id = Number(value);
53
+ if (Number.isFinite(id) && id > 0) {
54
+ return String(id);
55
+ }
56
+ return undefined;
57
+ };
58
+ const mergePrimaryAndFallbackPayload = (primaryPayload, fallbackPayload) => {
59
+ const merged = {
60
+ ...primaryPayload,
61
+ ...fallbackPayload
62
+ };
63
+ // When fallback succeeds but does not include status fields, force success codes
64
+ // to avoid preserving negative primary codes in response.
65
+ if (!Object.prototype.hasOwnProperty.call(fallbackPayload, 'retcode')) {
66
+ merged.retcode = 0;
67
+ }
68
+ if (!Object.prototype.hasOwnProperty.call(fallbackPayload, 'code')) {
69
+ merged.code = 0;
70
+ }
71
+ if (!Object.prototype.hasOwnProperty.call(fallbackPayload, 'subcode')) {
72
+ merged.subcode = 0;
73
+ }
74
+ return merged;
75
+ };
76
+ const resolveSongIdBySongmid = async ({ songmid, loginUin }) => {
77
+ const response = await (0, UCommon_1.default)({
78
+ method: 'get',
79
+ params: {
80
+ format: 'json',
81
+ data: {
82
+ comm: {
83
+ uin: loginUin,
84
+ ct: 24,
85
+ cv: 0
86
+ },
87
+ songinfo: {
88
+ method: 'get_song_detail_yqq',
89
+ param: {
90
+ song_type: 0,
91
+ song_mid: songmid,
92
+ song_id: 0
93
+ },
94
+ module: 'music.pf_song_detail_svr'
95
+ }
96
+ }
97
+ },
98
+ option: {}
99
+ });
100
+ const data = (response?.data || {});
101
+ return normalizeSongId(data?.songinfo?.data?.track_info?.id ||
102
+ data?.songinfo?.data?.trackInfo?.id ||
103
+ data?.track_info?.id);
104
+ };
105
+ const fetchLyricByMusicu = async ({ songmid, songid, loginUin, cookie }) => {
106
+ const reqPayload = {
107
+ req_0: {
108
+ module: 'music.musichallSong.PlayLyricInfo',
109
+ method: 'GetPlayLyricInfo',
110
+ param: {
111
+ songMID: songmid || '',
112
+ songID: Number(songid || 0),
113
+ trans_t: 0,
114
+ roma_t: 0,
115
+ qrc_t: 0,
116
+ crypt: 1,
117
+ lrc_t: 0,
118
+ interval: 0
119
+ }
120
+ },
121
+ loginUin,
122
+ comm: {
123
+ uin: loginUin,
124
+ format: 'json',
125
+ ct: 24,
126
+ cv: 0
127
+ }
128
+ };
129
+ const controller = new AbortController();
130
+ const timer = setTimeout(() => controller.abort(), MUSICU_TIMEOUT_MS);
131
+ const headers = {
132
+ 'Content-Type': 'application/json',
133
+ Referer: 'https://y.qq.com/portal/player.html',
134
+ Origin: 'https://y.qq.com'
135
+ };
136
+ if (cookie) {
137
+ headers.Cookie = cookie;
138
+ }
139
+ const response = await (async () => {
140
+ try {
141
+ return await fetch('https://u.y.qq.com/cgi-bin/musicu.fcg', {
142
+ method: 'POST',
143
+ headers,
144
+ body: JSON.stringify(reqPayload),
145
+ signal: controller.signal
146
+ });
147
+ }
148
+ finally {
149
+ clearTimeout(timer);
150
+ }
151
+ })();
152
+ const raw = await response.text();
153
+ const upstream = JSON.parse(raw || '{}');
154
+ return (upstream?.req_0?.data || upstream?.PlayLyricInfo?.data || {});
155
+ };
9
156
  exports.default = async ({ method = 'get', params = {}, option = {}, isFormat = false }) => {
157
+ const cookie = getCookieFromOptions(option);
158
+ const loginUin = resolveUin(cookie);
159
+ const songmid = typeof params.songmid === 'string'
160
+ ? params.songmid
161
+ : undefined;
162
+ const songid = typeof params.songid === 'string'
163
+ ? params.songid
164
+ : undefined;
10
165
  const data = Object.assign(params, {
11
166
  format: 'json',
12
167
  outCharset: 'utf-8',
13
- pcachetime: Date.now()
168
+ pcachetime: Date.now(),
169
+ loginUin
14
170
  });
15
171
  const options = Object.assign(option, {
172
+ headers: Object.assign({}, option?.headers || {}, {
173
+ referer: 'https://y.qq.com/portal/player.html',
174
+ host: 'c.y.qq.com'
175
+ }),
16
176
  params: data
17
177
  });
18
- return (0, apiResponse_1.handleApi)((0, request_1.default)({
19
- url: '/lyric/fcgi-bin/fcg_query_lyric_new.fcg',
20
- method: method,
21
- options
22
- }), {
23
- transformData: (resData) => {
24
- const lyricString = resData && resData.lyric && Buffer.from(resData.lyric, 'base64').toString();
25
- const lyric = isFormat && lyricString ? (0, lyricParse_1.lyricParse)(lyricString) : lyricString;
26
- return {
27
- ...resData,
28
- lyric
29
- };
178
+ try {
179
+ const primaryResponse = await (0, y_common_1.default)({
180
+ url: '/lyric/fcgi-bin/fcg_query_lyric_new.fcg',
181
+ method: method,
182
+ options
183
+ });
184
+ let payload = (primaryResponse?.data || {});
185
+ if (hasNegativeBizCode(payload)) {
186
+ try {
187
+ let fallbackPayload = await fetchLyricByMusicu({
188
+ songmid,
189
+ songid,
190
+ loginUin,
191
+ cookie
192
+ });
193
+ if (hasNegativeBizCode(fallbackPayload) && !normalizeSongId(songid) && songmid) {
194
+ const resolvedSongId = await resolveSongIdBySongmid({ songmid, loginUin });
195
+ if (resolvedSongId) {
196
+ fallbackPayload = await fetchLyricByMusicu({
197
+ songmid,
198
+ songid: resolvedSongId,
199
+ loginUin,
200
+ cookie
201
+ });
202
+ }
203
+ }
204
+ if (Object.keys(fallbackPayload).length > 0 && !hasNegativeBizCode(fallbackPayload)) {
205
+ payload = mergePrimaryAndFallbackPayload(payload, fallbackPayload);
206
+ }
207
+ }
208
+ catch {
209
+ // keep primary payload when fallback request fails
210
+ }
30
211
  }
31
- });
212
+ return {
213
+ status: 200,
214
+ body: {
215
+ response: normalizeLyricResponse(payload, Boolean(isFormat))
216
+ }
217
+ };
218
+ }
219
+ catch (error) {
220
+ const normalizedError = error instanceof Error
221
+ ? {
222
+ name: error.name,
223
+ message: error.message
224
+ }
225
+ : {
226
+ name: 'Error',
227
+ message: 'Internal server error'
228
+ };
229
+ return {
230
+ status: 500,
231
+ body: {
232
+ error: normalizedError
233
+ }
234
+ };
235
+ }
32
236
  };
@@ -113,6 +113,7 @@ exports.default = async ({ method = 'get', params = {}, option = {} }) => {
113
113
  const guid = String(config_1._guid || '1429839143');
114
114
  const cookie = getCookieFromOptions(option);
115
115
  const uin = resolveUin(cookie);
116
+ const authst = (0, cookieResolver_1.extractCookieValue)(cookie, 'qqmusic_key');
116
117
  const fileType = FILE_TYPE_MAP[quality];
117
118
  const filename = songmidList.map(item => `${fileType.prefix}${item}${mediaId || item}${fileType.suffix}`);
118
119
  const requestPayload = {
@@ -126,7 +127,8 @@ exports.default = async ({ method = 'get', params = {}, option = {} }) => {
126
127
  songtype: [0],
127
128
  uin,
128
129
  loginflag: 1,
129
- platform: '20'
130
+ platform: '20',
131
+ ...(authst ? { authst } : {})
130
132
  }
131
133
  },
132
134
  loginUin: uin,
@@ -139,7 +141,6 @@ exports.default = async ({ method = 'get', params = {}, option = {} }) => {
139
141
  };
140
142
  const upstreamParams = {
141
143
  format: 'json',
142
- sign: 'zzannc1o6o9b4i971602f3554385022046ab796512b7012',
143
144
  data: JSON.stringify(requestPayload)
144
145
  };
145
146
  try {
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sansenjian/qq-music-api",
3
- "version": "2.2.9",
3
+ "version": "2.3.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -10,8 +10,20 @@
10
10
  "qq-music-api": "dist/app.js"
11
11
  },
12
12
  "files": [
13
- "dist",
13
+ "dist/api",
14
+ "dist/app.js",
15
+ "dist/config/service-config.js",
16
+ "dist/config/service-config.json",
17
+ "dist/config/user-info.js",
18
+ "dist/index.js",
19
+ "dist/koaApp.js",
20
+ "dist/middlewares",
21
+ "dist/module",
22
+ "dist/package.json",
23
+ "dist/routers",
24
+ "dist/util",
14
25
  "docs-dist",
26
+ "public",
15
27
  "README.md",
16
28
  "CHANGELOG.md",
17
29
  "LICENSE"
@@ -23,7 +35,7 @@
23
35
  },
24
36
  "scripts": {
25
37
  "dev": "tsx app.ts",
26
- "build": "tsc",
38
+ "build": "rimraf dist && tsc",
27
39
  "start": "node dist/app.js",
28
40
  "vercel-build": "npm run build && npm run docs:build && (cp -r public dist/ 2>/dev/null || true)",
29
41
  "docs:build": "node scripts/generate-version.js && vitepress build docs",
@@ -66,18 +78,11 @@
66
78
  "homepage": "https://github.com/sansenjian/qq-music-api#readme",
67
79
  "dependencies": {
68
80
  "@koa/router": "^15.3.1",
69
- "axios": "^1.13.6",
70
- "date-fns": "^4.1.0",
71
- "is-generator-function": "1.0.10",
81
+ "axios": "^1.16.0",
82
+ "chalk": "^4.1.0",
72
83
  "koa": "^2.16.1",
73
84
  "koa-bodyparser": "^4.4.1",
74
- "koa-static": "^5.0.0",
75
- "reflect-metadata": "^0.2.2"
76
- },
77
- "pnpm": {
78
- "overrides": {
79
- "is-generator-function": "^1.0.10"
80
- }
85
+ "koa-static": "^5.0.0"
81
86
  },
82
87
  "devDependencies": {
83
88
  "@commitlint/cli": "^18.0.0",
@@ -91,7 +96,6 @@
91
96
  "@types/supertest": "^7.2.0",
92
97
  "@typescript-eslint/eslint-plugin": "^6.21.0",
93
98
  "@typescript-eslint/parser": "^6.21.0",
94
- "chalk": "^4.1.0",
95
99
  "conventional-changelog-cli": "^4.0.0",
96
100
  "eslint": "^8.57.0",
97
101
  "eslint-config-standard": "^17.0.0",
@@ -111,7 +115,7 @@
111
115
  "ts-node": "^10.9.2",
112
116
  "tsx": "^4.21.0",
113
117
  "typescript": "5.7.3",
114
- "vitepress": "^1.6.4",
118
+ "vitepress": "^2.0.0-alpha.17",
115
119
  "vue": "^3.5.29"
116
120
  },
117
121
  "engines": {
@@ -1,15 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const module_1 = require("../../module");
4
+ const cookieResolver_1 = require("../../util/cookieResolver");
4
5
  const controller = async (ctx, next) => {
5
- const songmid = Array.isArray(ctx.query.songmid) ? ctx.query.songmid[0] : ctx.query.songmid;
6
+ const songmid = Array.isArray(ctx.query.songmid)
7
+ ? ctx.query.songmid[0]
8
+ : (ctx.query.songmid || ctx.params.songmid);
6
9
  const rawIsFormat = Array.isArray(ctx.query.isFormat) ? ctx.query.isFormat[0] : ctx.query.isFormat;
10
+ const { cookie: effectiveCookie } = (0, cookieResolver_1.resolveRequestCookie)(ctx);
11
+ const headers = {};
12
+ if (effectiveCookie) {
13
+ headers.Cookie = effectiveCookie;
14
+ }
7
15
  const props = {
8
16
  method: 'get',
9
17
  params: {
10
18
  songmid
11
19
  },
12
- option: {},
20
+ option: {
21
+ headers
22
+ },
13
23
  isFormat: rawIsFormat
14
24
  };
15
25
  if (songmid) {
@@ -47,6 +47,10 @@ const controller = async (ctx, next) => {
47
47
  })
48
48
  .catch(error => {
49
49
  console.log('error', error);
50
+ ctx.status = 502;
51
+ ctx.body = {
52
+ error: error instanceof Error ? error.message : error
53
+ };
50
54
  });
51
55
  };
52
56
  exports.default = controller;