@sansenjian/qq-music-api 2.2.7 → 2.2.10

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 (121) hide show
  1. package/CHANGELOG.md +23 -1
  2. package/README.md +2 -0
  3. package/dist/api/index.js +9 -0
  4. package/dist/app.js +30 -45
  5. package/dist/config/service-config.js +37 -0
  6. package/dist/index.js +13 -1
  7. package/dist/jest.config.js +14 -2
  8. package/dist/koaApp.js +45 -0
  9. package/dist/middlewares/fallback-middleware.js +29 -0
  10. package/dist/module/apis/music/getLyric.js +220 -16
  11. package/dist/module/apis/music/getMusicPlay.js +187 -0
  12. package/dist/module/apis/u_common.js +8 -2
  13. package/dist/module/apis/user/getUserPlaylists.js +5 -5
  14. package/dist/module/index.js +3 -1
  15. package/dist/package.json +5 -2
  16. package/dist/routers/context/batchGetSongInfo.js +11 -16
  17. package/dist/routers/context/batchGetSongLists.js +18 -20
  18. package/dist/routers/context/getAlbumInfo.js +10 -17
  19. package/dist/routers/context/getComments.js +12 -19
  20. package/dist/routers/context/getDailyRecommend.js +5 -17
  21. package/dist/routers/context/getHotkey.js +7 -8
  22. package/dist/routers/context/getLyric.js +12 -2
  23. package/dist/routers/context/getMusicPlay.js +23 -81
  24. package/dist/routers/context/getMv.js +16 -22
  25. package/dist/routers/context/getMvPlay.js +48 -49
  26. package/dist/routers/context/getPersonalRecommend.js +13 -25
  27. package/dist/routers/context/getPlaylistTags.js +20 -26
  28. package/dist/routers/context/getRanks.js +9 -16
  29. package/dist/routers/context/getSingerAlbum.js +16 -22
  30. package/dist/routers/context/getSingerHotsong.js +16 -22
  31. package/dist/routers/context/getSingerList.js +9 -21
  32. package/dist/routers/context/getSongInfo.js +9 -16
  33. package/dist/routers/context/getSongListCategories.js +6 -7
  34. package/dist/routers/context/getSongListDetail.js +6 -8
  35. package/dist/routers/context/getUserAvatar.js +20 -33
  36. package/dist/routers/context/getUserPlaylists.js +6 -3
  37. package/dist/routers/context/index.js +94 -103
  38. package/dist/routers/util.js +31 -0
  39. package/dist/scripts/run-tests-with-flags.js +139 -0
  40. package/dist/util/cookie.js +15 -7
  41. package/dist/util/cookieResolver.js +66 -0
  42. package/dist/util/request.js +15 -6
  43. package/docs-dist/404.html +2 -2
  44. package/docs-dist/CHANGELOG-ARCHITECTURE.html +6 -6
  45. package/docs-dist/COOKIE_CONFIG_GUIDE.html +6 -6
  46. package/docs-dist/FALLBACK_MODE_GUIDE.html +101 -0
  47. package/docs-dist/README.html +6 -6
  48. package/docs-dist/TEST_USER_PLAYLISTS.html +6 -6
  49. package/docs-dist/USER_AVATAR_GUIDE.html +6 -6
  50. package/docs-dist/api/comments.html +6 -6
  51. package/docs-dist/api/index.html +6 -6
  52. package/docs-dist/api/music.html +6 -6
  53. package/docs-dist/api/other.html +6 -6
  54. package/docs-dist/api/playlist.html +6 -6
  55. package/docs-dist/api/rank.html +6 -6
  56. package/docs-dist/api/search.html +6 -6
  57. package/docs-dist/api/singer.html +6 -6
  58. package/docs-dist/api/user.html +6 -6
  59. package/docs-dist/assets/{CHANGELOG-ARCHITECTURE.md.BOe0ZtyR.js → CHANGELOG-ARCHITECTURE.md.DV9Xr7ve.js} +1 -1
  60. package/docs-dist/assets/{CHANGELOG-ARCHITECTURE.md.BOe0ZtyR.lean.js → CHANGELOG-ARCHITECTURE.md.DV9Xr7ve.lean.js} +1 -1
  61. package/docs-dist/assets/{COOKIE_CONFIG_GUIDE.md.D68AwXR2.js → COOKIE_CONFIG_GUIDE.md.B2-aTdcH.js} +1 -1
  62. package/docs-dist/assets/{COOKIE_CONFIG_GUIDE.md.D68AwXR2.lean.js → COOKIE_CONFIG_GUIDE.md.B2-aTdcH.lean.js} +1 -1
  63. package/docs-dist/assets/FALLBACK_MODE_GUIDE.md.0wqXqYxw.js +75 -0
  64. package/docs-dist/assets/FALLBACK_MODE_GUIDE.md.0wqXqYxw.lean.js +1 -0
  65. package/docs-dist/assets/{README.md.ZJQGJ1Gb.js → README.md.DFCMeLFa.js} +1 -1
  66. package/docs-dist/assets/{README.md.ZJQGJ1Gb.lean.js → README.md.DFCMeLFa.lean.js} +1 -1
  67. package/docs-dist/assets/{TEST_USER_PLAYLISTS.md.C02575X2.js → TEST_USER_PLAYLISTS.md.Bj0AVpHw.js} +1 -1
  68. package/docs-dist/assets/{TEST_USER_PLAYLISTS.md.C02575X2.lean.js → TEST_USER_PLAYLISTS.md.Bj0AVpHw.lean.js} +1 -1
  69. package/docs-dist/assets/{USER_AVATAR_GUIDE.md.BOqjn5Cm.js → USER_AVATAR_GUIDE.md.CGPI9GUj.js} +1 -1
  70. package/docs-dist/assets/{USER_AVATAR_GUIDE.md.BOqjn5Cm.lean.js → USER_AVATAR_GUIDE.md.CGPI9GUj.lean.js} +1 -1
  71. package/docs-dist/assets/{api_comments.md.DADvndEA.js → api_comments.md.CATvWhrg.js} +1 -1
  72. package/docs-dist/assets/{api_comments.md.DADvndEA.lean.js → api_comments.md.CATvWhrg.lean.js} +1 -1
  73. package/docs-dist/assets/{api_index.md.D5IASxxG.js → api_index.md.Dqx3qXyO.js} +1 -1
  74. package/docs-dist/assets/{api_index.md.D5IASxxG.lean.js → api_index.md.Dqx3qXyO.lean.js} +1 -1
  75. package/docs-dist/assets/{api_music.md.BgB8NmZq.js → api_music.md.D20_neZB.js} +1 -1
  76. package/docs-dist/assets/{api_music.md.BgB8NmZq.lean.js → api_music.md.D20_neZB.lean.js} +1 -1
  77. package/docs-dist/assets/{api_other.md.BkRWXX2z.js → api_other.md.CXyEsl8R.js} +1 -1
  78. package/docs-dist/assets/{api_other.md.BkRWXX2z.lean.js → api_other.md.CXyEsl8R.lean.js} +1 -1
  79. package/docs-dist/assets/{api_playlist.md.Dc0hTrZ4.js → api_playlist.md.CyLdLRR9.js} +1 -1
  80. package/docs-dist/assets/{api_playlist.md.Dc0hTrZ4.lean.js → api_playlist.md.CyLdLRR9.lean.js} +1 -1
  81. package/docs-dist/assets/{api_rank.md.DRisCFyT.js → api_rank.md.Z3xyYG_S.js} +1 -1
  82. package/docs-dist/assets/{api_rank.md.DRisCFyT.lean.js → api_rank.md.Z3xyYG_S.lean.js} +1 -1
  83. package/docs-dist/assets/{api_search.md.DNnMUZK0.js → api_search.md.D_lbFmYo.js} +1 -1
  84. package/docs-dist/assets/{api_search.md.DNnMUZK0.lean.js → api_search.md.D_lbFmYo.lean.js} +1 -1
  85. package/docs-dist/assets/{api_singer.md.DCmuxQkk.js → api_singer.md.BbyYE88D.js} +1 -1
  86. package/docs-dist/assets/{api_singer.md.DCmuxQkk.lean.js → api_singer.md.BbyYE88D.lean.js} +1 -1
  87. package/docs-dist/assets/{api_user.md.Cjm9GG3z.js → api_user.md.4WdmTXIB.js} +1 -1
  88. package/docs-dist/assets/{api_user.md.Cjm9GG3z.lean.js → api_user.md.4WdmTXIB.lean.js} +1 -1
  89. package/docs-dist/assets/{app.Dx_1wB58.js → app.2f7gcITE.js} +1 -1
  90. package/docs-dist/assets/chunks/@localSearchIndexroot.D461xa5C.js +1 -0
  91. package/docs-dist/assets/chunks/{VPLocalSearchBox.DwKWtsdX.js → VPLocalSearchBox.BiPSl83v.js} +1 -1
  92. package/docs-dist/assets/chunks/framework.aJbMEiY9.js +19 -0
  93. package/docs-dist/assets/chunks/{theme.pGVgJ9Cx.js → theme.BrMPT0hE.js} +2 -2
  94. package/docs-dist/assets/{guide_architecture.md.DGtNyuMH.js → guide_architecture.md.D_46khUI.js} +1 -1
  95. package/docs-dist/assets/{guide_architecture.md.DGtNyuMH.lean.js → guide_architecture.md.D_46khUI.lean.js} +1 -1
  96. package/docs-dist/assets/{guide_authentication.md.mtI5LfCw.js → guide_authentication.md.nCiAu07w.js} +1 -1
  97. package/docs-dist/assets/{guide_authentication.md.mtI5LfCw.lean.js → guide_authentication.md.nCiAu07w.lean.js} +1 -1
  98. package/docs-dist/assets/{guide_index.md.B-0SG46T.js → guide_index.md.gLozHqz5.js} +1 -1
  99. package/docs-dist/assets/{guide_index.md.B-0SG46T.lean.js → guide_index.md.gLozHqz5.lean.js} +1 -1
  100. package/docs-dist/assets/{guide_installation.md.k-KpAfxv.js → guide_installation.md.BUDl8zk1.js} +1 -1
  101. package/docs-dist/assets/guide_installation.md.BUDl8zk1.lean.js +1 -0
  102. package/docs-dist/assets/{guide_quickstart.md.Bff_KFOD.js → guide_quickstart.md.COQUzUN9.js} +1 -1
  103. package/docs-dist/assets/guide_quickstart.md.COQUzUN9.lean.js +1 -0
  104. package/docs-dist/assets/{index.md.xrs-uIyo.js → index.md.DBZfQ2kF.js} +1 -1
  105. package/docs-dist/assets/{index.md.xrs-uIyo.lean.js → index.md.DBZfQ2kF.lean.js} +1 -1
  106. package/docs-dist/assets/{reference_response-format.md.DKYTK6uJ.js → reference_response-format.md.yrdeqFUN.js} +1 -1
  107. package/docs-dist/assets/{reference_response-format.md.DKYTK6uJ.lean.js → reference_response-format.md.yrdeqFUN.lean.js} +1 -1
  108. package/docs-dist/guide/architecture.html +6 -6
  109. package/docs-dist/guide/authentication.html +6 -6
  110. package/docs-dist/guide/index.html +6 -6
  111. package/docs-dist/guide/installation.html +6 -6
  112. package/docs-dist/guide/quickstart.html +6 -6
  113. package/docs-dist/hashmap.json +1 -1
  114. package/docs-dist/index.html +5 -5
  115. package/docs-dist/reference/response-format.html +6 -6
  116. package/docs-dist/version.json +3 -3
  117. package/package.json +5 -2
  118. package/docs-dist/assets/chunks/@localSearchIndexroot.CMY5EIwU.js +0 -1
  119. package/docs-dist/assets/chunks/framework.o40iizuP.js +0 -19
  120. package/docs-dist/assets/guide_installation.md.k-KpAfxv.lean.js +0 -1
  121. package/docs-dist/assets/guide_quickstart.md.Bff_KFOD.lean.js +0 -1
@@ -0,0 +1,187 @@
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 UCommon_1 = __importDefault(require("../UCommon/UCommon"));
7
+ const config_1 = require("../../config");
8
+ const cookieResolver_1 = require("../../../util/cookieResolver");
9
+ const DEFAULT_QUALITY = '128';
10
+ const FILE_TYPE_MAP = {
11
+ m4a: { prefix: 'C400', suffix: '.m4a' },
12
+ 128: { prefix: 'M500', suffix: '.mp3' },
13
+ 320: { prefix: 'M800', suffix: '.mp3' },
14
+ ape: { prefix: 'A000', suffix: '.ape' },
15
+ flac: { prefix: 'F000', suffix: '.flac' }
16
+ };
17
+ const normalizeFirstValue = (value) => {
18
+ if (Array.isArray(value)) {
19
+ return normalizeFirstValue(value[0]);
20
+ }
21
+ if (typeof value !== 'string') {
22
+ return undefined;
23
+ }
24
+ const normalized = value.trim();
25
+ return normalized || undefined;
26
+ };
27
+ const normalizeSongmidList = (songmid) => {
28
+ const values = Array.isArray(songmid) ? songmid : [songmid];
29
+ return values
30
+ .flatMap(value => {
31
+ if (typeof value !== 'string')
32
+ return [];
33
+ return value.split(',');
34
+ })
35
+ .map(item => item.trim())
36
+ .filter(Boolean);
37
+ };
38
+ const normalizeQuality = (value) => {
39
+ const quality = normalizeFirstValue(value) || String(value || '');
40
+ if (quality === 'm4a' || quality === 'ape' || quality === 'flac') {
41
+ return quality;
42
+ }
43
+ const numericQuality = Number(quality);
44
+ if (numericQuality === 320) {
45
+ return '320';
46
+ }
47
+ if (numericQuality === 128) {
48
+ return '128';
49
+ }
50
+ return DEFAULT_QUALITY;
51
+ };
52
+ const pickPlayableDomain = (sip) => {
53
+ if (!Array.isArray(sip))
54
+ return '';
55
+ const urls = sip.filter((item) => typeof item === 'string' && item.length > 0);
56
+ return (urls.find(url => !url.startsWith('http://ws')) ||
57
+ urls.find(url => url.startsWith('https://')) ||
58
+ urls[0] ||
59
+ '');
60
+ };
61
+ const joinUrl = (domain, path) => {
62
+ if (domain.endsWith('/') && path.startsWith('/')) {
63
+ return `${domain}${path.slice(1)}`;
64
+ }
65
+ if (!domain.endsWith('/') && !path.startsWith('/')) {
66
+ return `${domain}/${path}`;
67
+ }
68
+ return `${domain}${path}`;
69
+ };
70
+ const buildPlayUrl = (domain, info, guid) => {
71
+ if (!domain)
72
+ return '';
73
+ if (info.purl) {
74
+ return joinUrl(domain, info.purl);
75
+ }
76
+ // Fallback for payloads where purl is empty but vkey/filename are present.
77
+ if (info.vkey && info.filename) {
78
+ return `${joinUrl(domain, info.filename)}?vkey=${info.vkey}&guid=${guid}&fromtag=66`;
79
+ }
80
+ return '';
81
+ };
82
+ const resolveUin = (cookie) => {
83
+ const defaultUin = String(global.userInfo?.uin || global.userInfo?.loginUin || '0');
84
+ return (0, cookieResolver_1.extractUinFromCookie)(cookie) || defaultUin;
85
+ };
86
+ const getCookieFromOptions = (option) => {
87
+ if (!option || typeof option !== 'object') {
88
+ return undefined;
89
+ }
90
+ const headers = option.headers;
91
+ if (!headers || typeof headers !== 'object') {
92
+ return undefined;
93
+ }
94
+ const cookie = headers.Cookie || headers.cookie;
95
+ return typeof cookie === 'string' ? cookie : undefined;
96
+ };
97
+ exports.default = async ({ method = 'get', params = {}, option = {} }) => {
98
+ const musicParams = params;
99
+ const songmidList = normalizeSongmidList(musicParams.songmid);
100
+ if (songmidList.length === 0) {
101
+ return {
102
+ status: 400,
103
+ body: {
104
+ data: {
105
+ message: 'no songmid'
106
+ }
107
+ }
108
+ };
109
+ }
110
+ const justPlayUrl = (normalizeFirstValue(musicParams.resType) || 'play') === 'play';
111
+ const mediaId = normalizeFirstValue(musicParams.mediaId);
112
+ const quality = normalizeQuality(musicParams.quality);
113
+ const guid = String(config_1._guid || '1429839143');
114
+ const cookie = getCookieFromOptions(option);
115
+ const uin = resolveUin(cookie);
116
+ const fileType = FILE_TYPE_MAP[quality];
117
+ const filename = songmidList.map(item => `${fileType.prefix}${item}${mediaId || item}${fileType.suffix}`);
118
+ const requestPayload = {
119
+ req_0: {
120
+ module: 'vkey.GetVkeyServer',
121
+ method: 'CgiGetVkey',
122
+ param: {
123
+ filename,
124
+ guid,
125
+ songmid: songmidList,
126
+ songtype: [0],
127
+ uin,
128
+ loginflag: 1,
129
+ platform: '20'
130
+ }
131
+ },
132
+ loginUin: uin,
133
+ comm: {
134
+ uin,
135
+ format: 'json',
136
+ ct: 24,
137
+ cv: 0
138
+ }
139
+ };
140
+ const upstreamParams = {
141
+ format: 'json',
142
+ sign: 'zzannc1o6o9b4i971602f3554385022046ab796512b7012',
143
+ data: JSON.stringify(requestPayload)
144
+ };
145
+ try {
146
+ const response = await (0, UCommon_1.default)({
147
+ method: method,
148
+ params: upstreamParams,
149
+ option
150
+ });
151
+ const upstreamData = response.data;
152
+ const domain = pickPlayableDomain(upstreamData?.req_0?.data?.sip);
153
+ const midurlinfo = Array.isArray(upstreamData?.req_0?.data?.midurlinfo)
154
+ ? upstreamData.req_0.data.midurlinfo
155
+ : [];
156
+ const playUrl = {};
157
+ const midInfoMap = new Map();
158
+ midurlinfo.forEach(item => {
159
+ if (item.songmid) {
160
+ midInfoMap.set(item.songmid, item);
161
+ }
162
+ });
163
+ songmidList.forEach(songmid => {
164
+ const item = midInfoMap.get(songmid);
165
+ const url = item ? buildPlayUrl(domain, item, guid) : '';
166
+ playUrl[songmid] = {
167
+ url,
168
+ error: url ? undefined : '\u6682\u65e0\u64ad\u653e\u94fe\u63a5'
169
+ };
170
+ });
171
+ upstreamData.playUrl = playUrl;
172
+ return {
173
+ status: 200,
174
+ body: {
175
+ data: justPlayUrl ? { playUrl } : upstreamData
176
+ }
177
+ };
178
+ }
179
+ catch (error) {
180
+ return {
181
+ status: 502,
182
+ body: {
183
+ error: error instanceof Error ? error.message : error
184
+ }
185
+ };
186
+ }
187
+ };
@@ -38,7 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  const request_1 = __importDefault(require("../../util/request"));
40
40
  const config = __importStar(require("../config"));
41
- exports.default = ({ options = {}, method = 'get' }) => {
41
+ exports.default = ({ options = {}, method = 'get', customCookie }) => {
42
42
  const opts = { ...options };
43
43
  // Merge commonParams into params for query string
44
44
  opts.params = { ...config.commonParams, ...(opts.params || {}) };
@@ -52,5 +52,11 @@ exports.default = ({ options = {}, method = 'get' }) => {
52
52
  const logOpts = { ...opts, headers: { ...opts.headers, cookie: '[REDACTED]' } };
53
53
  console.log('https://u.y.qq.com/cgi-bin/musicu.fcg', { opts: logOpts });
54
54
  }
55
- return (0, request_1.default)('https://u.y.qq.com/cgi-bin/musicu.fcg', method, opts, 'u');
55
+ return (0, request_1.default)({
56
+ url: 'https://u.y.qq.com/cgi-bin/musicu.fcg',
57
+ method: method,
58
+ options: opts,
59
+ isUUrl: 'u',
60
+ cookie: customCookie
61
+ });
56
62
  };
@@ -59,7 +59,7 @@ const getErrorMessage = (payload) => {
59
59
  // 获取用户创建的歌单
60
60
  // 注意:此接口需要有效的 QQ 音乐 Cookie 才能正常工作
61
61
  const getUserPlaylists = async (params) => {
62
- const { uin, offset = 0, limit = 30 } = params;
62
+ const { uin, offset = 0, limit = 30, cookie } = params;
63
63
  // 使用 c6.y.qq.com/rsc/fcgi-bin/fcg_get_profile_homepage.fcg 接口
64
64
  // 这是通过 Chrome DevTools 抓包发现的实际使用的接口
65
65
  const url = 'https://c6.y.qq.com/rsc/fcgi-bin/fcg_get_profile_homepage.fcg';
@@ -71,13 +71,14 @@ const getUserPlaylists = async (params) => {
71
71
  offset,
72
72
  limit,
73
73
  pageOffset,
74
- hasGlobalCookie: Boolean(global.userInfo?.cookie),
75
- cookieLength: global.userInfo?.cookie?.length || 0
74
+ hasCookie: Boolean(cookie),
75
+ cookieLength: cookie?.length || 0
76
76
  });
77
77
  const response = await (0, request_1.default)({
78
78
  url,
79
79
  method: 'GET',
80
80
  isUUrl: 'u',
81
+ cookie,
81
82
  options: {
82
83
  params: {
83
84
  _: Date.now(),
@@ -100,8 +101,7 @@ const getUserPlaylists = async (params) => {
100
101
  loginUin: Number.parseInt(uin, 10)
101
102
  },
102
103
  headers: {
103
- Referer: `https://y.qq.com/portal/profile.html?uin=${uin}`,
104
- Cookie: global.userInfo?.cookie || ''
104
+ Referer: `https://y.qq.com/portal/profile.html?uin=${uin}`
105
105
  }
106
106
  }
107
107
  });
@@ -3,7 +3,7 @@ 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
- exports.getUserLikedSongs = exports.getUserAvatar = exports.getUserPlaylists = exports.checkQQLoginQr = exports.getQQLoginQr = exports.getTopLists = exports.UCommon = exports.getComments = exports.getAlbumInfo = exports.getLyric = exports.getDigitalAlbumLists = exports.getRadioLists = exports.getSingerStarNum = exports.getSingerDesc = exports.getSingerMv = exports.getSimilarSinger = exports.getMvByTag = exports.songListDetail = exports.songListCategories = exports.songLists = exports.getSmartbox = exports.getSearchByKey = exports.getHotKey = exports.downloadQQMusic = void 0;
6
+ exports.getUserLikedSongs = exports.getUserAvatar = exports.getUserPlaylists = exports.checkQQLoginQr = exports.getQQLoginQr = exports.getTopLists = exports.UCommon = exports.getComments = exports.getAlbumInfo = exports.getMusicPlay = exports.getLyric = exports.getDigitalAlbumLists = exports.getRadioLists = exports.getSingerStarNum = exports.getSingerDesc = exports.getSingerMv = exports.getSimilarSinger = exports.getMvByTag = exports.songListDetail = exports.songListCategories = exports.songLists = exports.getSmartbox = exports.getSearchByKey = exports.getHotKey = exports.downloadQQMusic = void 0;
7
7
  const downloadQQMusic_1 = __importDefault(require("./apis/downloadQQMusic"));
8
8
  exports.downloadQQMusic = downloadQQMusic_1.default;
9
9
  // search
@@ -41,6 +41,8 @@ exports.getDigitalAlbumLists = getDigitalAlbumLists_1.default;
41
41
  // music
42
42
  const getLyric_1 = __importDefault(require("./apis/music/getLyric"));
43
43
  exports.getLyric = getLyric_1.default;
44
+ const getMusicPlay_1 = __importDefault(require("./apis/music/getMusicPlay"));
45
+ exports.getMusicPlay = getMusicPlay_1.default;
44
46
  // album
45
47
  const getAlbumInfo_1 = __importDefault(require("./apis/album/getAlbumInfo"));
46
48
  exports.getAlbumInfo = getAlbumInfo_1.default;
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sansenjian/qq-music-api",
3
- "version": "2.2.7",
3
+ "version": "2.2.10",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -31,6 +31,9 @@
31
31
  "test:watch": "jest --watch",
32
32
  "test:coverage": "jest --coverage",
33
33
  "test:verbose": "jest --verbose",
34
+ "test:unit": "jest --testPathPattern=tests/unit",
35
+ "test:flags": "tsx scripts/run-tests-with-flags.ts all",
36
+ "test:flags:unit": "tsx scripts/run-tests-with-flags.ts unit",
34
37
  "docs:dev": "vitepress dev docs --port 9611",
35
38
  "docs:preview": "vitepress preview docs",
36
39
  "commit-push": "./scripts/commit-push.sh",
@@ -112,6 +115,6 @@
112
115
  "vue": "^3.5.29"
113
116
  },
114
117
  "engines": {
115
- "node": ">=20.0.0"
118
+ "node": "22.x"
116
119
  }
117
120
  }
@@ -1,16 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const module_1 = require("../../module");
4
- const controller = async (ctx, next) => {
4
+ const util_1 = require("../util");
5
+ const apiResponse_1 = require("../../util/apiResponse");
6
+ const batchGetSongInfoController = (0, util_1.withErrorHandler)(async (ctx) => {
5
7
  const { songs } = ctx.request.body || {};
6
- const params = Object.assign({
8
+ const params = {
7
9
  format: 'json',
8
10
  inCharset: 'utf8',
9
11
  outCharset: 'utf-8',
10
12
  notice: 0,
11
13
  platform: 'yqq.json',
12
14
  needNewCode: 0
13
- });
15
+ };
14
16
  const props = {
15
17
  method: 'get',
16
18
  option: {},
@@ -18,7 +20,7 @@ const controller = async (ctx, next) => {
18
20
  };
19
21
  const data = await Promise.all((songs || []).map(async (song) => {
20
22
  const [song_mid, song_id = ''] = song;
21
- return await (0, module_1.UCommon)({
23
+ const response = await (0, module_1.UCommon)({
22
24
  ...props,
23
25
  params: {
24
26
  ...params,
@@ -38,16 +40,9 @@ const controller = async (ctx, next) => {
38
40
  }
39
41
  }
40
42
  }
41
- }).then(res => res.data);
43
+ });
44
+ return response.data;
42
45
  }));
43
- Object.assign(ctx, {
44
- status: 200,
45
- body: {
46
- response: {
47
- code: 0,
48
- data
49
- }
50
- }
51
- });
52
- };
53
- exports.default = controller;
46
+ (0, util_1.setApiResponse)(ctx, (0, apiResponse_1.customResponse)({ response: { code: 0, data } }, 200));
47
+ });
48
+ exports.default = batchGetSongInfoController;
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const module_1 = require("../../module");
4
- const controller = async (ctx, next) => {
4
+ const util_1 = require("../util");
5
+ const apiResponse_1 = require("../../util/apiResponse");
6
+ const batchGetSongListsController = (0, util_1.withErrorHandler)(async (ctx) => {
5
7
  const { limit: ein = 19, page: sin = 0, sortId = 5, categoryIds = [10000000] } = ctx.request.body || {};
6
8
  const params = {
7
9
  sortId,
@@ -13,25 +15,21 @@ const controller = async (ctx, next) => {
13
15
  option: {},
14
16
  params
15
17
  };
16
- const data = await Promise.all(categoryIds.map(async (categoryId) => await (0, module_1.songLists)({
17
- ...props,
18
- params: {
19
- ...params,
20
- categoryId
21
- }
22
- }).then(res => {
23
- if (res.body.response && +res.body.response.code === 0) {
24
- return res.body.response.data;
18
+ const data = await Promise.all(categoryIds.map(async (categoryId) => {
19
+ const result = await (0, module_1.songLists)({
20
+ ...props,
21
+ params: {
22
+ ...params,
23
+ categoryId
24
+ }
25
+ });
26
+ if (result.body.response && +result.body.response.code === 0) {
27
+ return result.body.response.data;
25
28
  }
26
29
  else {
27
- return res.body.response;
28
- }
29
- })));
30
- Object.assign(ctx, {
31
- body: {
32
- status: 200,
33
- data
30
+ return result.body.response;
34
31
  }
35
- });
36
- };
37
- exports.default = controller;
32
+ }));
33
+ (0, util_1.setApiResponse)(ctx, (0, apiResponse_1.customResponse)({ status: 200, data }, 200));
34
+ });
35
+ exports.default = batchGetSongListsController;
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const module_1 = require("../../module");
4
- const controller = async (ctx, next) => {
4
+ const util_1 = require("../util");
5
+ const apiResponse_1 = require("../../util/apiResponse");
6
+ const getAlbumInfoController = (0, util_1.withErrorHandler)(async (ctx) => {
5
7
  const { albummid } = ctx.query;
6
8
  const props = {
7
9
  method: 'get',
@@ -10,20 +12,11 @@ const controller = async (ctx, next) => {
10
12
  },
11
13
  option: {}
12
14
  };
13
- if (albummid) {
14
- const { status, body } = await (0, module_1.getAlbumInfo)(props);
15
- Object.assign(ctx, {
16
- status,
17
- body
18
- });
15
+ if (!albummid) {
16
+ (0, util_1.setApiResponse)(ctx, (0, apiResponse_1.errorResponse)('no albummid', 400));
17
+ return;
19
18
  }
20
- else {
21
- ctx.status = 400;
22
- ctx.body = {
23
- data: {
24
- message: 'no albummid'
25
- }
26
- };
27
- }
28
- };
29
- exports.default = controller;
19
+ const result = await (0, module_1.getAlbumInfo)(props);
20
+ (0, util_1.setApiResponse)(ctx, result);
21
+ });
22
+ exports.default = getAlbumInfoController;
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const module_1 = require("../../module");
4
- const controller = async (ctx, next) => {
4
+ const util_1 = require("../util");
5
+ const apiResponse_1 = require("../../util/apiResponse");
6
+ const getCommentsController = (0, util_1.withErrorHandler)(async (ctx) => {
5
7
  const { id, pagesize = 25, pagenum = 0, cid = 205360772, cmd = 8, reqtype = 2, biztype = 1, rootcommentid = !pagenum && '' } = ctx.query;
6
8
  const checkrootcommentid = !pagenum ? true : !!rootcommentid;
7
- const params = Object.assign({
9
+ const params = {
8
10
  cid,
9
11
  reqtype,
10
12
  biztype,
@@ -13,26 +15,17 @@ const controller = async (ctx, next) => {
13
15
  pagenum,
14
16
  pagesize,
15
17
  lasthotcommentid: rootcommentid
16
- });
18
+ };
17
19
  const props = {
18
20
  method: 'get',
19
21
  params,
20
22
  option: {}
21
23
  };
22
- if (id && checkrootcommentid) {
23
- const { status, body } = await (0, module_1.getComments)(props);
24
- Object.assign(ctx, {
25
- status,
26
- body
27
- });
28
- }
29
- else {
30
- ctx.status = 400;
31
- ctx.body = {
32
- data: {
33
- message: 'Don\'t have id or rootcommentid'
34
- }
35
- };
24
+ if (!id || !checkrootcommentid) {
25
+ (0, util_1.setApiResponse)(ctx, (0, apiResponse_1.errorResponse)('Don\'t have id or rootcommentid', 400));
26
+ return;
36
27
  }
37
- };
38
- exports.default = controller;
28
+ const result = await (0, module_1.getComments)(props);
29
+ (0, util_1.setApiResponse)(ctx, result);
30
+ });
31
+ exports.default = getCommentsController;
@@ -7,31 +7,19 @@ exports.getDailyRecommendController = getDailyRecommendController;
7
7
  exports.getPrivateFMController = getPrivateFMController;
8
8
  exports.getNewSongsController = getNewSongsController;
9
9
  const getDailyRecommend_1 = __importDefault(require("../../module/apis/recommend/getDailyRecommend"));
10
- /**
11
- * 获取每日推荐歌曲
12
- */
10
+ const cookieResolver_1 = require("../../util/cookieResolver");
13
11
  async function getDailyRecommendController(ctx) {
14
- const { cookie } = ctx.query;
15
- // 处理数组类型,取第一个值
16
- const normalizedCookie = Array.isArray(cookie) ? cookie[0] : cookie;
17
- const result = await getDailyRecommend_1.default.getDailyRecommend(normalizedCookie);
12
+ const { cookie } = (0, cookieResolver_1.resolveRequestCookie)(ctx);
13
+ const result = await getDailyRecommend_1.default.getDailyRecommend(cookie);
18
14
  ctx.status = result.status;
19
15
  ctx.body = result.body;
20
16
  }
21
- /**
22
- * 获取私人 FM
23
- */
24
17
  async function getPrivateFMController(ctx) {
25
- const { cookie } = ctx.query;
26
- // 处理数组类型,取第一个值
27
- const normalizedCookie = Array.isArray(cookie) ? cookie[0] : cookie;
28
- const result = await getDailyRecommend_1.default.getPrivateFM(normalizedCookie);
18
+ const { cookie } = (0, cookieResolver_1.resolveRequestCookie)(ctx);
19
+ const result = await getDailyRecommend_1.default.getPrivateFM(cookie);
29
20
  ctx.status = result.status;
30
21
  ctx.body = result.body;
31
22
  }
32
- /**
33
- * 获取新歌速递
34
- */
35
23
  async function getNewSongsController(ctx) {
36
24
  const { areaId = '5', limit = '20' } = ctx.query;
37
25
  const result = await getDailyRecommend_1.default.getNewSongs(Number(areaId), Number(limit));
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const module_1 = require("../../module");
4
- exports.default = async (ctx, next) => {
4
+ const util_1 = require("../util");
5
+ const getHotkeyController = (0, util_1.withErrorHandler)(async (ctx) => {
5
6
  const props = {
6
7
  method: 'get',
7
8
  params: {},
@@ -10,12 +11,10 @@ exports.default = async (ctx, next) => {
10
11
  if (process.env.DEBUG === 'true') {
11
12
  console.log('[getHotkey] controller props:', props);
12
13
  }
13
- const { status, body } = await (0, module_1.getHotKey)(props);
14
+ const result = await (0, module_1.getHotKey)(props);
14
15
  if (process.env.DEBUG === 'true') {
15
- console.log('[getHotkey] controller response status:', status);
16
+ console.log('[getHotkey] controller response status:', result.status);
16
17
  }
17
- Object.assign(ctx, {
18
- status,
19
- body
20
- });
21
- };
18
+ (0, util_1.setApiResponse)(ctx, result);
19
+ });
20
+ exports.default = getHotkeyController;
@@ -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) {