@ikenxuan/amagi 5.10.0 → 5.11.0
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.
- package/dist/default/index.cjs +1457 -1316
- package/dist/default/index.d.cts +32 -25
- package/dist/default/index.d.ts +32 -25
- package/dist/default/index.js +1456 -1316
- package/package.json +1 -1
package/dist/default/index.cjs
CHANGED
|
@@ -11,10 +11,10 @@ let log4js = require("log4js");
|
|
|
11
11
|
log4js = require_chunk.__toESM(log4js);
|
|
12
12
|
let axios = require("axios");
|
|
13
13
|
axios = require_chunk.__toESM(axios);
|
|
14
|
-
let node_crypto = require("node:crypto");
|
|
15
|
-
node_crypto = require_chunk.__toESM(node_crypto);
|
|
16
14
|
let zod = require("zod");
|
|
17
15
|
zod = require_chunk.__toESM(zod);
|
|
16
|
+
let node_crypto = require("node:crypto");
|
|
17
|
+
node_crypto = require_chunk.__toESM(node_crypto);
|
|
18
18
|
let __ikenxuan_xhshow_ts = require("@ikenxuan/xhshow-ts");
|
|
19
19
|
let express = require("express");
|
|
20
20
|
express = require_chunk.__toESM(express);
|
|
@@ -171,206 +171,6 @@ const logMiddleware = (pathsToLog) => {
|
|
|
171
171
|
};
|
|
172
172
|
};
|
|
173
173
|
|
|
174
|
-
//#endregion
|
|
175
|
-
//#region src/model/networks.ts
|
|
176
|
-
/**
|
|
177
|
-
* 清理User-Agent中的Edge标识,确保请求兼容性
|
|
178
|
-
* @param userAgent - 原始User-Agent字符串
|
|
179
|
-
* @returns 清理后的User-Agent字符串
|
|
180
|
-
*/
|
|
181
|
-
const cleanUserAgent = (userAgent) => {
|
|
182
|
-
return userAgent.replace(/\s+Edg\/[\d\.]+/g, "");
|
|
183
|
-
};
|
|
184
|
-
/**
|
|
185
|
-
* 执行网络请求并返回数据
|
|
186
|
-
* @param config - axios请求配置
|
|
187
|
-
* @returns 响应数据
|
|
188
|
-
*/
|
|
189
|
-
const fetchData = async (config) => {
|
|
190
|
-
try {
|
|
191
|
-
const cleanedConfig = { ...config };
|
|
192
|
-
if (cleanedConfig.headers && cleanedConfig.headers["User-Agent"]) cleanedConfig.headers["User-Agent"] = cleanUserAgent(cleanedConfig.headers["User-Agent"]);
|
|
193
|
-
return (await (0, axios.default)({
|
|
194
|
-
...cleanedConfig,
|
|
195
|
-
validateStatus: () => true
|
|
196
|
-
})).data;
|
|
197
|
-
} catch (error) {
|
|
198
|
-
if (error instanceof axios.AxiosError) {
|
|
199
|
-
logger.error("网络请求失败:", error.message);
|
|
200
|
-
throw error;
|
|
201
|
-
}
|
|
202
|
-
throw error;
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
const normalizeHeaders = (headers) => {
|
|
206
|
-
if (headers && typeof headers.toJSON === "function") return headers.toJSON();
|
|
207
|
-
return headers ?? {};
|
|
208
|
-
};
|
|
209
|
-
const fetchResponse = async (config) => {
|
|
210
|
-
try {
|
|
211
|
-
const cleanedConfig = { ...config };
|
|
212
|
-
if (cleanedConfig.headers && cleanedConfig.headers["User-Agent"]) cleanedConfig.headers["User-Agent"] = cleanUserAgent(cleanedConfig.headers["User-Agent"]);
|
|
213
|
-
return await (0, axios.default)({
|
|
214
|
-
...cleanedConfig,
|
|
215
|
-
validateStatus: () => true
|
|
216
|
-
});
|
|
217
|
-
} catch (error) {
|
|
218
|
-
if (error instanceof axios.AxiosError) throw error;
|
|
219
|
-
throw new Error("网络请求失败");
|
|
220
|
-
}
|
|
221
|
-
};
|
|
222
|
-
/**
|
|
223
|
-
* 获取响应头和数据
|
|
224
|
-
* @param config - axios请求配置
|
|
225
|
-
* @returns 包含headers和data的对象
|
|
226
|
-
*/
|
|
227
|
-
const getHeadersAndData = async (config) => {
|
|
228
|
-
try {
|
|
229
|
-
const response = await fetchResponse(config);
|
|
230
|
-
return {
|
|
231
|
-
headers: normalizeHeaders(response.headers),
|
|
232
|
-
data: response.data
|
|
233
|
-
};
|
|
234
|
-
} catch (error) {
|
|
235
|
-
logger.error("获取响应头和数据失败:", error);
|
|
236
|
-
return {
|
|
237
|
-
headers: {},
|
|
238
|
-
data: {}
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
//#endregion
|
|
244
|
-
//#region src/platform/defaultConfigs.ts
|
|
245
|
-
/**
|
|
246
|
-
* 根据User-Agent生成对应的Sec-Ch-Ua值
|
|
247
|
-
* @param userAgent - 用户代理字符串
|
|
248
|
-
* @returns 对应的Sec-Ch-Ua值
|
|
249
|
-
*/
|
|
250
|
-
const generateSecChUa = (userAgent) => {
|
|
251
|
-
const chromeMatch = userAgent.match(/Chrome\/(\d+)/);
|
|
252
|
-
const chromeVersion = chromeMatch ? chromeMatch[1] : "125";
|
|
253
|
-
return `"Not)A;Brand";v="8", "Chromium";v="${chromeVersion}", "Google Chrome";v="${chromeVersion}"`;
|
|
254
|
-
};
|
|
255
|
-
/**
|
|
256
|
-
* 抖音平台默认请求配置
|
|
257
|
-
* @param cookie - 用户Cookie
|
|
258
|
-
* @param requestConfig - 外部请求配置(优先级最高)
|
|
259
|
-
* @returns 合并后的请求配置
|
|
260
|
-
*/
|
|
261
|
-
const getDouyinDefaultConfig = (cookie, requestConfig) => {
|
|
262
|
-
let finalUserAgent = requestConfig?.headers?.["User-Agent"] ?? "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36";
|
|
263
|
-
finalUserAgent = finalUserAgent.replace(/\s+Edg\/[\d\.]+/g, "");
|
|
264
|
-
const defHeaders = {
|
|
265
|
-
Accept: "application/json, text/plain, */*",
|
|
266
|
-
"Accept-Encoding": "gzip, deflate, br, zstd",
|
|
267
|
-
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
|
268
|
-
Cookie: cookie ? cookie.replace(/\s+/g, "") : "",
|
|
269
|
-
Priority: "u=1, i",
|
|
270
|
-
Referer: "https://www.douyin.com/",
|
|
271
|
-
"Sec-Ch-Ua": generateSecChUa(finalUserAgent),
|
|
272
|
-
"Sec-Ch-Ua-Mobile": "?0",
|
|
273
|
-
"Sec-Ch-Ua-Platform": "\"Windows\"",
|
|
274
|
-
"Sec-Fetch-Dest": "empty",
|
|
275
|
-
"Sec-Fetch-Mode": "cors",
|
|
276
|
-
"Sec-Fetch-Site": "same-origin",
|
|
277
|
-
"User-Agent": finalUserAgent
|
|
278
|
-
};
|
|
279
|
-
return {
|
|
280
|
-
method: "GET",
|
|
281
|
-
timeout: 1e4,
|
|
282
|
-
...requestConfig,
|
|
283
|
-
headers: {
|
|
284
|
-
...defHeaders,
|
|
285
|
-
...requestConfig?.headers ?? {}
|
|
286
|
-
}
|
|
287
|
-
};
|
|
288
|
-
};
|
|
289
|
-
/**
|
|
290
|
-
* B站平台默认请求配置
|
|
291
|
-
* @param cookie - 用户Cookie
|
|
292
|
-
* @param requestConfig - 外部请求配置(优先级最高)
|
|
293
|
-
* @returns 合并后的请求配置
|
|
294
|
-
*/
|
|
295
|
-
const getBilibiliDefaultConfig = (cookie, requestConfig) => {
|
|
296
|
-
const defHeaders = {
|
|
297
|
-
Accept: "application/json, text/plain, */*",
|
|
298
|
-
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
|
299
|
-
"Accept-Encoding": "gzip, deflate, br",
|
|
300
|
-
Origin: "https://www.bilibili.com",
|
|
301
|
-
Referer: "https://www.bilibili.com/",
|
|
302
|
-
Priority: "u=1, i",
|
|
303
|
-
"Sec-Ch-Ua": "\"Microsoft Edge\";v=\"141\", \"Chromium\";v=\"141\", \"Not_A Brand\";v=\"24\"",
|
|
304
|
-
"Sec-Ch-Ua-Mobile": "?0",
|
|
305
|
-
"Sec-Ch-Ua-Platform": "\"Windows\"",
|
|
306
|
-
"Sec-Fetch-Dest": "empty",
|
|
307
|
-
"Sec-Fetch-Mode": "cors",
|
|
308
|
-
"Sec-Fetch-Site": "same-site",
|
|
309
|
-
Cookie: cookie ? cookie.replace(/\s+/g, "") : ""
|
|
310
|
-
};
|
|
311
|
-
return {
|
|
312
|
-
method: "GET",
|
|
313
|
-
timeout: 1e4,
|
|
314
|
-
...requestConfig,
|
|
315
|
-
headers: {
|
|
316
|
-
...defHeaders,
|
|
317
|
-
...requestConfig?.headers ?? {}
|
|
318
|
-
}
|
|
319
|
-
};
|
|
320
|
-
};
|
|
321
|
-
/**
|
|
322
|
-
* 快手平台默认请求配置
|
|
323
|
-
* @param cookie - 用户Cookie
|
|
324
|
-
* @param requestConfig - 外部请求配置(优先级最高)
|
|
325
|
-
* @returns 合并后的请求配置
|
|
326
|
-
*/
|
|
327
|
-
const getKuaishouDefaultConfig = (cookie, requestConfig) => {
|
|
328
|
-
const defHeaders = {
|
|
329
|
-
Referer: "https://www.kuaishou.com/new-reco",
|
|
330
|
-
Origin: "https://www.kuaishou.com",
|
|
331
|
-
Accept: "application/json, text/plain, */*",
|
|
332
|
-
"Accept-Encoding": "gzip, deflate, br, zstd",
|
|
333
|
-
"Content-Type": "application/json",
|
|
334
|
-
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
|
335
|
-
Priority: "u=0, i",
|
|
336
|
-
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0",
|
|
337
|
-
Cookie: cookie ? cookie.replace(/\s+/g, "") : ""
|
|
338
|
-
};
|
|
339
|
-
return {
|
|
340
|
-
method: "POST",
|
|
341
|
-
timeout: 1e4,
|
|
342
|
-
...requestConfig,
|
|
343
|
-
headers: {
|
|
344
|
-
...defHeaders,
|
|
345
|
-
...requestConfig?.headers ?? {}
|
|
346
|
-
}
|
|
347
|
-
};
|
|
348
|
-
};
|
|
349
|
-
/**
|
|
350
|
-
* 获取小红书默认配置
|
|
351
|
-
* @param cookie - 用户Cookie
|
|
352
|
-
* @returns 小红书请求配置
|
|
353
|
-
*/
|
|
354
|
-
const getXiaohongshuDefaultConfig = (cookie) => {
|
|
355
|
-
return { headers: {
|
|
356
|
-
accept: "application/json, text/plain, */*",
|
|
357
|
-
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
|
358
|
-
"cache-control": "no-cache",
|
|
359
|
-
"content-type": "application/json;charset=UTF-8",
|
|
360
|
-
pragma: "no-cache",
|
|
361
|
-
priority: "u=1, i",
|
|
362
|
-
referer: "https://www.xiaohongshu.com/",
|
|
363
|
-
"sec-ch-ua": "\"Microsoft Edge\";v=\"141\", \"Not?A_Brand\";v=\"8\", \"Chromium\";v=\"141\"",
|
|
364
|
-
"sec-ch-ua-mobile": "?0",
|
|
365
|
-
"sec-ch-ua-platform": "\"Windows\"",
|
|
366
|
-
"sec-fetch-dest": "empty",
|
|
367
|
-
"sec-fetch-mode": "cors",
|
|
368
|
-
"sec-fetch-site": "same-site",
|
|
369
|
-
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0",
|
|
370
|
-
cookie: cookie ?? ""
|
|
371
|
-
} };
|
|
372
|
-
};
|
|
373
|
-
|
|
374
174
|
//#endregion
|
|
375
175
|
//#region src/types/NetworksConfigType.ts
|
|
376
176
|
/** 未知错误 */
|
|
@@ -523,1219 +323,1499 @@ let xiaohongshuAPIErrorCode = /* @__PURE__ */ function(xiaohongshuAPIErrorCode$1
|
|
|
523
323
|
}({});
|
|
524
324
|
|
|
525
325
|
//#endregion
|
|
526
|
-
//#region src/
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
return
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
return
|
|
553
|
-
}
|
|
554
|
-
/** 指定评论的回复 */
|
|
555
|
-
指定评论的回复(data$1) {
|
|
556
|
-
return `https://api.bilibili.com/x/v2/reply/reply?type=${data$1.type}&oid=${data$1.oid}&root=${data$1.root}&ps=${data$1.number}`;
|
|
557
|
-
}
|
|
558
|
-
表情列表() {
|
|
559
|
-
return "https://api.bilibili.com/x/emote/user/panel/web?business=reply&web_location=0.0";
|
|
560
|
-
}
|
|
561
|
-
番剧明细(data$1) {
|
|
562
|
-
if (data$1.ep_id) return `https://api.bilibili.com/pgc/view/web/season?ep_id=${data$1.ep_id}`;
|
|
563
|
-
else if (data$1.season_id) return `https://api.bilibili.com/pgc/view/web/season?season_id=${data$1.season_id}`;
|
|
564
|
-
else throw new Error("拟造接口地址出错,缺少 ep_id 或 season_id 参数");
|
|
565
|
-
}
|
|
566
|
-
番剧视频流信息(data$1) {
|
|
567
|
-
return `https://api.bilibili.com/pgc/player/web/playurl?cid=${data$1.cid}&ep_id=${data$1.ep_id}`;
|
|
568
|
-
}
|
|
569
|
-
用户空间动态(data$1) {
|
|
570
|
-
return `https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?host_mid=${data$1.host_mid}&dm_img_switch=0&&features=itemOpusStyle,listOnlyfans,opusBigCover,onlyfansVote,forwardListHidden,decorationCard,commentsNewVersion,onlyfansAssetsV2,ugcDelete,onlyfansQaCard`;
|
|
571
|
-
}
|
|
572
|
-
动态详情(data$1) {
|
|
573
|
-
return `https://api.bilibili.com/x/polymer/web-dynamic/v1/detail?id=${data$1.dynamic_id}&features=itemOpusStyle,opusBigCover,onlyfansVote,endFooterHidden,decorationCard,onlyfansAssetsV2,ugcDelete,onlyfansQaCard,editable,opusPrivateVisible,avatarAutoTheme`;
|
|
574
|
-
}
|
|
575
|
-
动态卡片信息(data$1) {
|
|
576
|
-
return `https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/get_dynamic_detail?dynamic_id=${data$1.dynamic_id}`;
|
|
577
|
-
}
|
|
578
|
-
用户名片信息(data$1) {
|
|
579
|
-
return `https://api.bilibili.com/x/web-interface/card?mid=${data$1.host_mid}&photo=true`;
|
|
580
|
-
}
|
|
581
|
-
直播间信息(data$1) {
|
|
582
|
-
return `https://api.live.bilibili.com/room/v1/Room/get_info?room_id=${data$1.room_id}`;
|
|
583
|
-
}
|
|
584
|
-
直播间初始化信息(data$1) {
|
|
585
|
-
return `https://api.live.bilibili.com/room/v1/Room/room_init?id=${data$1.room_id}`;
|
|
586
|
-
}
|
|
587
|
-
申请二维码() {
|
|
588
|
-
return "https://passport.bilibili.com/x/passport-login/web/qrcode/generate";
|
|
589
|
-
}
|
|
590
|
-
二维码状态(data$1) {
|
|
591
|
-
return `https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key=${data$1.qrcode_key}`;
|
|
592
|
-
}
|
|
593
|
-
获取UP主总播放量(data$1) {
|
|
594
|
-
return `https://api.bilibili.com/x/space/upstat?mid=${data$1.host_mid}`;
|
|
595
|
-
}
|
|
596
|
-
专栏正文内容(data$1) {
|
|
597
|
-
return `https://api.bilibili.com/x/article/view?id=${data$1.id}`;
|
|
598
|
-
}
|
|
599
|
-
专栏显示卡片信息(data$1) {
|
|
600
|
-
return `https://api.bilibili.com/x/article/cards?ids=${Array.isArray(data$1.ids) ? data$1.ids.join(",") : data$1.ids}`;
|
|
601
|
-
}
|
|
602
|
-
专栏文章基本信息(data$1) {
|
|
603
|
-
return `https://api.bilibili.com/x/article/viewinfo?id=${data$1.id}`;
|
|
604
|
-
}
|
|
605
|
-
文集基本信息(data$1) {
|
|
606
|
-
return `https://api.bilibili.com/x/article/list/web/articles?id=${data$1.id}`;
|
|
326
|
+
//#region src/validation/utils.ts
|
|
327
|
+
function smartNumber(errorMessage, minValue = 1, isInteger = false) {
|
|
328
|
+
if (isInteger) return zod.default.coerce.number({ error: errorMessage }).int({ error: `${errorMessage.replace("不能为空", "")}必须是整数,不能包含小数` }).min(minValue, { error: `${errorMessage.replace("不能为空", "")}必须大于等于${minValue}` });
|
|
329
|
+
else return zod.default.coerce.number({ error: errorMessage }).min(minValue, { error: `${errorMessage.replace("不能为空", "")}必须大于等于${minValue}` });
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* 智能正整数转换器 - 专门用于正整数类型的转换
|
|
333
|
+
* @param errorMessage - 自定义错误信息
|
|
334
|
+
* @returns Zod正整数验证器
|
|
335
|
+
*/
|
|
336
|
+
const smartPositiveInteger = (errorMessage) => {
|
|
337
|
+
return smartNumber(errorMessage, 1, true);
|
|
338
|
+
};
|
|
339
|
+
/**
|
|
340
|
+
* 从页面HTML中提取用户信息
|
|
341
|
+
* @param html - 包含用户页面HTML的字符串
|
|
342
|
+
* @returns 提取到的用户信息对象或null
|
|
343
|
+
*/
|
|
344
|
+
const extractCreatorInfoFromHtml = (html) => {
|
|
345
|
+
const match = html.match(/<script>window\.__INITIAL_STATE__=(.+)<\/script>/m);
|
|
346
|
+
if (!match) return null;
|
|
347
|
+
try {
|
|
348
|
+
const jsonStr = match[1].replace(/:undefined/g, ":null");
|
|
349
|
+
return JSON.parse(jsonStr)?.user?.userPageData ?? null;
|
|
350
|
+
} catch (error) {
|
|
351
|
+
console.error("解析用户信息失败:", error);
|
|
352
|
+
return null;
|
|
607
353
|
}
|
|
608
354
|
};
|
|
609
|
-
/** 该类下的所有方法只会返回拼接好参数后的 Url 地址,需要手动请求该地址以获取数据 */
|
|
610
|
-
const bilibiliApiUrls = new BiLiBiLiAPI();
|
|
611
355
|
|
|
612
356
|
//#endregion
|
|
613
|
-
//#region src/
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
357
|
+
//#region src/validation/bilibili.ts
|
|
358
|
+
const BilibiliVideoParamsSchema = zod.default.object({
|
|
359
|
+
methodType: zod.default.literal("单个视频作品数据", { error: "方法类型必须是\"单个视频作品数据\"" }),
|
|
360
|
+
bvid: zod.default.string({ error: "BVID必须是字符串" }).min(1, { error: "BVID不能为空" })
|
|
361
|
+
});
|
|
362
|
+
const BilibiliVideoDownloadParamsSchema = zod.default.object({
|
|
363
|
+
methodType: zod.default.literal("单个视频下载信息数据", { error: "方法类型必须是\"单个视频下载信息数据\"" }),
|
|
364
|
+
avid: smartNumber("AVID不能为空", 1, true),
|
|
365
|
+
cid: smartNumber("CID不能为空", 1, true)
|
|
366
|
+
});
|
|
367
|
+
const BilibiliCommentParamsSchema = zod.default.object({
|
|
368
|
+
methodType: zod.default.literal("评论数据", { error: "方法类型必须是\"评论数据\"" }),
|
|
369
|
+
oid: zod.default.string({ error: "OID必须是字符串" }).min(1, { error: "OID不能为空" }),
|
|
370
|
+
type: smartNumber("评论类型不能为空", 1, true).refine((val) => [
|
|
371
|
+
1,
|
|
372
|
+
2,
|
|
373
|
+
4,
|
|
374
|
+
5,
|
|
375
|
+
6,
|
|
376
|
+
7,
|
|
377
|
+
8,
|
|
378
|
+
9,
|
|
379
|
+
10,
|
|
380
|
+
11,
|
|
381
|
+
12,
|
|
382
|
+
13,
|
|
383
|
+
14,
|
|
384
|
+
15,
|
|
385
|
+
16,
|
|
386
|
+
17,
|
|
387
|
+
18,
|
|
388
|
+
19,
|
|
389
|
+
20,
|
|
390
|
+
21,
|
|
391
|
+
22,
|
|
392
|
+
33
|
|
393
|
+
].includes(val), { error: "无效的评论区类型" }),
|
|
394
|
+
number: zod.default.coerce.number({ error: "评论数量必须是数字" }).int({ error: "评论数量必须是整数" }).positive({ error: "评论数量必须是正数" }).default(20).optional(),
|
|
395
|
+
pn: zod.default.coerce.number({ error: "页码必须是数字" }).int({ error: "页码必须是整数" }).positive({ error: "页码必须是正数" }).default(1).optional()
|
|
396
|
+
});
|
|
397
|
+
const BilibiliCommentReplyParamsSchema = zod.default.object({
|
|
398
|
+
methodType: zod.default.literal("指定评论的回复", { error: "方法类型必须是\"指定评论的回复\"" }),
|
|
399
|
+
oid: zod.default.string({ error: "OID必须是字符串" }).min(1, { error: "OID不能为空" }),
|
|
400
|
+
type: smartNumber("评论类型不能为空", 1, true).refine((val) => [
|
|
401
|
+
1,
|
|
402
|
+
2,
|
|
403
|
+
4,
|
|
404
|
+
5,
|
|
405
|
+
6,
|
|
406
|
+
7,
|
|
407
|
+
8,
|
|
408
|
+
9,
|
|
409
|
+
10,
|
|
410
|
+
11,
|
|
411
|
+
12,
|
|
412
|
+
13,
|
|
413
|
+
14,
|
|
414
|
+
15,
|
|
415
|
+
16,
|
|
416
|
+
17,
|
|
417
|
+
18,
|
|
418
|
+
19,
|
|
419
|
+
20,
|
|
420
|
+
21,
|
|
421
|
+
22,
|
|
422
|
+
33
|
|
423
|
+
].includes(val), { error: "无效的评论区类型" }),
|
|
424
|
+
root: zod.default.string({ error: "根评论ID必须是字符串" }).min(1, { error: "根评论ID不能为空" }),
|
|
425
|
+
number: zod.default.coerce.number({ error: "评论数量必须是数字" }).int({ error: "评论数量必须是整数" }).positive({ error: "评论数量必须是正数" }).default(20).optional(),
|
|
426
|
+
pn: zod.default.coerce.number({ error: "页码必须是数字" }).int({ error: "页码必须是整数" }).positive({ error: "页码必须是正数" }).default(1).optional()
|
|
427
|
+
});
|
|
428
|
+
const BilibiliUserParamsSchema = zod.default.object({
|
|
429
|
+
methodType: zod.default.enum([
|
|
430
|
+
"用户主页数据",
|
|
431
|
+
"用户主页动态列表数据",
|
|
432
|
+
"获取UP主总播放量"
|
|
433
|
+
], { error: "方法类型必须是指定的枚举值之一" }),
|
|
434
|
+
host_mid: smartNumber("UP主UID不能为空", 1, true)
|
|
435
|
+
});
|
|
436
|
+
const BilibiliEmojiParamsSchema = zod.default.object({ methodType: zod.default.literal("Emoji数据", { error: "方法类型必须是\"Emoji数据\"" }) });
|
|
437
|
+
const BilibiliBangumiInfoParamsSchema = zod.default.object({
|
|
438
|
+
methodType: zod.default.literal("番剧基本信息数据", { error: "方法类型必须是\"番剧基本信息数据\"" }),
|
|
439
|
+
ep_id: zod.default.string({ error: "番剧EP ID必须是字符串" }).min(1, { error: "番剧EP ID不能为空" }).optional(),
|
|
440
|
+
season_id: zod.default.string({ error: "番剧季度ID必须是字符串" }).optional()
|
|
441
|
+
}).refine((data$1) => data$1.ep_id ?? data$1.season_id, {
|
|
442
|
+
error: "ep_id 和 season_id 至少需要提供一个",
|
|
443
|
+
path: ["ep_id"]
|
|
444
|
+
});
|
|
445
|
+
const BilibiliBangumiStreamParamsSchema = zod.default.object({
|
|
446
|
+
methodType: zod.default.literal("番剧下载信息数据", { error: "方法类型必须是\"番剧下载信息数据\"" }),
|
|
447
|
+
cid: smartNumber("CID不能为空", 1, true),
|
|
448
|
+
ep_id: zod.default.string({ error: "番剧EP ID必须是字符串" }).min(1, { error: "番剧EP ID不能为空" })
|
|
449
|
+
});
|
|
450
|
+
const BilibiliDynamicParamsSchema = zod.default.object({
|
|
451
|
+
methodType: zod.default.enum(["动态详情数据", "动态卡片数据"], { error: "方法类型必须是\"动态详情数据\"或\"动态卡片数据\"" }),
|
|
452
|
+
dynamic_id: zod.default.string({ error: "动态ID必须是字符串" }).min(1, { error: "动态ID不能为空" })
|
|
453
|
+
});
|
|
454
|
+
const BilibiliLiveParamsSchema = zod.default.object({
|
|
455
|
+
methodType: zod.default.enum(["直播间信息", "直播间初始化信息"], { error: "方法类型必须是\"直播间信息\"或\"直播间初始化信息\"" }),
|
|
456
|
+
room_id: zod.default.string({ error: "直播间ID必须是字符串" }).min(1, { error: "直播间ID不能为空" })
|
|
457
|
+
});
|
|
458
|
+
const BilibiliLoginParamsSchema = zod.default.object({ methodType: zod.default.literal("登录基本信息", { error: "方法类型必须是\"登录基本信息\"" }) });
|
|
459
|
+
const BilibiliQrcodeParamsSchema = zod.default.object({ methodType: zod.default.literal("申请二维码", { error: "方法类型必须是\"申请二维码\"" }) });
|
|
460
|
+
const BilibiliQrcodeStatusParamsSchema = zod.default.object({
|
|
461
|
+
methodType: zod.default.literal("二维码状态", { error: "方法类型必须是\"二维码状态\"" }),
|
|
462
|
+
qrcode_key: zod.default.string({ error: "二维码key必须是字符串" }).min(1, { error: "二维码key不能为空" })
|
|
463
|
+
});
|
|
464
|
+
const BilibiliAv2BvParamsSchema = zod.default.object({
|
|
465
|
+
methodType: zod.default.literal("AV转BV", { error: "方法类型必须是\"AV转BV\"" }),
|
|
466
|
+
avid: zod.default.coerce.number({ error: "AVID必须是数字" }).int({ error: "AVID必须是整数" }).positive({ error: "AVID必须是正数" })
|
|
467
|
+
});
|
|
468
|
+
const BilibiliBv2AvParamsSchema = zod.default.object({
|
|
469
|
+
methodType: zod.default.literal("BV转AV", { error: "方法类型必须是\"BV转AV\"" }),
|
|
470
|
+
bvid: zod.default.string({ error: "BVID必须是字符串" }).min(1, { error: "BVID不能为空" })
|
|
471
|
+
});
|
|
472
|
+
const BilibiliArticleParamsSchema = zod.default.object({
|
|
473
|
+
methodType: zod.default.literal("专栏正文内容", { error: "方法类型必须是\"专栏正文内容\"" }),
|
|
474
|
+
id: zod.default.string({ error: "专栏ID必须是字符串" }).min(1, { error: "专栏ID不能为空" })
|
|
475
|
+
});
|
|
476
|
+
const BilibiliArticleCardParamsSchema = zod.default.object({
|
|
477
|
+
methodType: zod.default.literal("专栏显示卡片信息", { error: "方法类型必须是\"专栏显示卡片信息\"" }),
|
|
478
|
+
ids: zod.default.union([zod.default.array(zod.default.string({ error: "被查询的 id 列表必须是字符串数组" })).min(1, { error: "被查询的 id 列表不能为空" }), zod.default.string({ error: "被查询的 id 列表必须是字符串" }).min(1, { error: "被查询的 id 列表不能为空" })])
|
|
479
|
+
});
|
|
480
|
+
const BilibiliArticleInfoParamsSchema = zod.default.object({
|
|
481
|
+
methodType: zod.default.literal("专栏文章基本信息", { error: "方法类型必须是\"专栏文章基本信息\"" }),
|
|
482
|
+
id: zod.default.string({ error: "专栏ID必须是字符串" }).min(1, { error: "专栏ID不能为空" })
|
|
483
|
+
});
|
|
484
|
+
const BilibiliColumnInfoParamsSchema = zod.default.object({
|
|
485
|
+
methodType: zod.default.literal("文集基本信息", { error: "方法类型必须是\"文集基本信息\"" }),
|
|
486
|
+
id: zod.default.string({ error: "文集ID必须是字符串" }).min(1, { error: "文集ID不能为空" })
|
|
487
|
+
});
|
|
488
|
+
const BilibiliValidationSchemas = {
|
|
489
|
+
单个视频作品数据: BilibiliVideoParamsSchema,
|
|
490
|
+
单个视频下载信息数据: BilibiliVideoDownloadParamsSchema,
|
|
491
|
+
评论数据: BilibiliCommentParamsSchema,
|
|
492
|
+
指定评论的回复: BilibiliCommentReplyParamsSchema,
|
|
493
|
+
用户主页数据: BilibiliUserParamsSchema,
|
|
494
|
+
用户主页动态列表数据: BilibiliUserParamsSchema,
|
|
495
|
+
Emoji数据: BilibiliEmojiParamsSchema,
|
|
496
|
+
番剧基本信息数据: BilibiliBangumiInfoParamsSchema,
|
|
497
|
+
番剧下载信息数据: BilibiliBangumiStreamParamsSchema,
|
|
498
|
+
动态详情数据: BilibiliDynamicParamsSchema,
|
|
499
|
+
动态卡片数据: BilibiliDynamicParamsSchema,
|
|
500
|
+
直播间信息: BilibiliLiveParamsSchema,
|
|
501
|
+
直播间初始化信息: BilibiliLiveParamsSchema,
|
|
502
|
+
登录基本信息: BilibiliLoginParamsSchema,
|
|
503
|
+
申请二维码: BilibiliQrcodeParamsSchema,
|
|
504
|
+
二维码状态: BilibiliQrcodeStatusParamsSchema,
|
|
505
|
+
获取UP主总播放量: BilibiliUserParamsSchema,
|
|
506
|
+
AV转BV: BilibiliAv2BvParamsSchema,
|
|
507
|
+
BV转AV: BilibiliBv2AvParamsSchema,
|
|
508
|
+
专栏正文内容: BilibiliArticleParamsSchema,
|
|
509
|
+
专栏显示卡片信息: BilibiliArticleCardParamsSchema,
|
|
510
|
+
专栏文章基本信息: BilibiliArticleInfoParamsSchema,
|
|
511
|
+
文集基本信息: BilibiliColumnInfoParamsSchema
|
|
624
512
|
};
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
513
|
+
const BilibiliMethodRoutes = {
|
|
514
|
+
单个视频作品数据: "/fetch_one_video",
|
|
515
|
+
单个视频下载信息数据: "/fetch_video_playurl",
|
|
516
|
+
评论数据: "/fetch_work_comments",
|
|
517
|
+
指定评论的回复: "/fetch_comment_reply",
|
|
518
|
+
用户主页数据: "/fetch_user_profile",
|
|
519
|
+
用户主页动态列表数据: "/fetch_user_dynamic",
|
|
520
|
+
Emoji数据: "/fetch_emoji_list",
|
|
521
|
+
番剧基本信息数据: "/fetch_bangumi_video_info",
|
|
522
|
+
番剧下载信息数据: "/fetch_bangumi_video_playurl",
|
|
523
|
+
动态详情数据: "/fetch_dynamic_info",
|
|
524
|
+
动态卡片数据: "/fetch_dynamic_card",
|
|
525
|
+
直播间信息: "/fetch_live_room_detail",
|
|
526
|
+
直播间初始化信息: "/fetch_liveroom_def",
|
|
527
|
+
登录基本信息: "/login_basic_info",
|
|
528
|
+
申请二维码: "/new_login_qrcode",
|
|
529
|
+
二维码状态: "/check_qrcode",
|
|
530
|
+
获取UP主总播放量: "/fetch_user_full_view",
|
|
531
|
+
AV转BV: "/av_to_bv",
|
|
532
|
+
BV转AV: "/bv_to_av",
|
|
533
|
+
专栏正文内容: "/fetch_article_content",
|
|
534
|
+
专栏显示卡片信息: "/fetch_article_card",
|
|
535
|
+
专栏文章基本信息: "/fetch_article_info",
|
|
536
|
+
文集基本信息: "/fetch_column_info"
|
|
636
537
|
};
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
538
|
+
|
|
539
|
+
//#endregion
|
|
540
|
+
//#region src/validation/douyin.ts
|
|
541
|
+
const DouyinWorkParamsSchema = zod.default.object({
|
|
542
|
+
methodType: zod.default.enum([
|
|
543
|
+
"视频作品数据",
|
|
544
|
+
"图集作品数据",
|
|
545
|
+
"合辑作品数据",
|
|
546
|
+
"聚合解析"
|
|
547
|
+
], { error: "方法类型必须是指定的枚举值之一" }),
|
|
548
|
+
aweme_id: zod.default.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" })
|
|
549
|
+
});
|
|
550
|
+
const DouyinCommentParamsSchema = zod.default.object({
|
|
551
|
+
methodType: zod.default.literal("评论数据", { error: "方法类型必须是\"评论数据\"" }),
|
|
552
|
+
aweme_id: zod.default.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" }),
|
|
553
|
+
number: smartPositiveInteger("评论数量必须是正整数").optional().default(50),
|
|
554
|
+
cursor: zod.default.coerce.number({ error: "游标必须是数字" }).int({ error: "游标必须是整数" }).min(0, { error: "游标不能小于0" }).default(0).optional()
|
|
555
|
+
});
|
|
556
|
+
const DouyinHotWordsParamsSchema = zod.default.object({
|
|
557
|
+
methodType: zod.default.literal("热点词数据", { error: "方法类型必须是\"热点词数据\"" }),
|
|
558
|
+
query: zod.default.string({ error: "搜索词必须是字符串" }).min(1, { error: "搜索词不能为空" })
|
|
559
|
+
});
|
|
560
|
+
const DouyinSearchParamsSchema = zod.default.object({
|
|
561
|
+
methodType: zod.default.literal("搜索数据", { error: "方法类型必须是\"搜索数据\"" }),
|
|
562
|
+
query: zod.default.string({ error: "搜索词必须是字符串" }).min(1, { error: "搜索词不能为空" }),
|
|
563
|
+
type: zod.default.enum([
|
|
564
|
+
"综合",
|
|
565
|
+
"用户",
|
|
566
|
+
"视频"
|
|
567
|
+
], { error: "搜索类型必须是\"综合\"、\"用户\"或\"视频\"" }).optional().default("综合"),
|
|
568
|
+
number: smartPositiveInteger("搜索数量必须是正整数").optional().default(10),
|
|
569
|
+
search_id: zod.default.string({ error: "搜索ID必须是字符串" }).optional()
|
|
570
|
+
});
|
|
571
|
+
const DouyinCommentReplyParamsSchema = zod.default.object({
|
|
572
|
+
methodType: zod.default.literal("指定评论回复数据", { error: "方法类型必须是\"指定评论回复数据\"" }),
|
|
573
|
+
aweme_id: zod.default.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" }),
|
|
574
|
+
comment_id: zod.default.string({ error: "评论ID必须z串" }).min(1, { error: "评论ID不能为空" }),
|
|
575
|
+
number: smartPositiveInteger("评论数量必须是正整数").optional().default(5),
|
|
576
|
+
cursor: zod.default.coerce.number({ error: "游标必须是数字" }).int({ error: "游标必须是整数" }).min(0, { error: "游标不能小于0" }).default(0).optional()
|
|
577
|
+
});
|
|
578
|
+
const DouyinUserParamsSchema = zod.default.object({
|
|
579
|
+
methodType: zod.default.enum(["用户主页数据", "用户主页视频列表数据"], { error: "方法类型必须是指定的枚举值之一" }),
|
|
580
|
+
sec_uid: zod.default.string({ error: "用户ID必须是字符串" }).min(1, { error: "用户ID不能为空" })
|
|
581
|
+
});
|
|
582
|
+
const DouyinMusicParamsSchema = zod.default.object({
|
|
583
|
+
methodType: zod.default.literal("音乐数据", { error: "方法类型必须是\"音乐数据\"" }),
|
|
584
|
+
music_id: zod.default.string({ error: "音乐ID必须是字符串" }).min(1, { error: "音乐ID不能为空" })
|
|
585
|
+
});
|
|
586
|
+
const DouyinLiveRoomParamsSchema = zod.default.object({
|
|
587
|
+
methodType: zod.default.literal("直播间信息数据", { error: "方法类型必须是\"直播间信息数据\"" }),
|
|
588
|
+
web_rid: zod.default.string({ error: "直播间ID必须是字符串" }).min(1, { error: "直播间ID不能为空" }),
|
|
589
|
+
room_id: zod.default.string({ error: "直播间ID必须是字符串" }).min(1, { error: "直播间ID不能为空" })
|
|
590
|
+
});
|
|
591
|
+
const DouyinQrcodeParamsSchema = zod.default.object({
|
|
592
|
+
methodType: zod.default.literal("申请二维码数据", { error: "方法类型必须是\"申请二维码数据\"" }),
|
|
593
|
+
verify_fp: zod.default.string({ error: "fp指纹必须是字符串" }).min(1, { error: "fp指纹不能为空" })
|
|
594
|
+
});
|
|
595
|
+
const DouyinEmojiListParamsSchema = zod.default.object({ methodType: zod.default.literal("Emoji数据", { error: "方法类型必须是\"Emoji数据\"" }) });
|
|
596
|
+
const DouyinEmojiProParamsSchema = zod.default.object({ methodType: zod.default.literal("动态表情数据", { error: "方法类型必须是\"动态表情数据\"" }) });
|
|
597
|
+
const DouyinDanmakuParamsSchema = zod.default.object({
|
|
598
|
+
methodType: zod.default.literal("弹幕数据", { error: "方法类型必须是\"弹幕数据\"" }),
|
|
599
|
+
aweme_id: zod.default.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" }),
|
|
600
|
+
start_time: zod.default.coerce.number({ error: "开始时间必须是数字" }).int({ error: "开始时间必须是整数" }).min(0, { error: "开始时间不能小于0" }).optional(),
|
|
601
|
+
end_time: zod.default.coerce.number({ error: "结束时间必须是数字" }).int({ error: "结束时间必须是整数" }).min(0, { error: "结束时间不能小于0" }).optional(),
|
|
602
|
+
duration: zod.default.coerce.number({ error: "视频时长必须是数字" }).int({ error: "视频时长必须是整数" }).min(0, { error: "视频时长不能小于0" })
|
|
603
|
+
}).refine((data$1) => {
|
|
604
|
+
if (data$1.end_time !== void 0) return data$1.end_time <= data$1.duration;
|
|
605
|
+
return true;
|
|
606
|
+
}, {
|
|
607
|
+
error: "获取弹幕区间的结束时间不能超过视频总时长",
|
|
608
|
+
path: ["end_time"]
|
|
609
|
+
}).refine((data$1) => {
|
|
610
|
+
if (data$1.start_time !== void 0 && data$1.end_time !== void 0) return data$1.start_time < data$1.end_time;
|
|
611
|
+
return true;
|
|
612
|
+
}, {
|
|
613
|
+
error: "获取弹幕区间的开始时间必须小于结束时间",
|
|
614
|
+
path: ["start_time"]
|
|
615
|
+
});
|
|
616
|
+
const DouyinValidationSchemas = {
|
|
617
|
+
文字作品数据: DouyinWorkParamsSchema,
|
|
618
|
+
聚合解析: DouyinWorkParamsSchema,
|
|
619
|
+
视频作品数据: DouyinWorkParamsSchema,
|
|
620
|
+
图集作品数据: DouyinWorkParamsSchema,
|
|
621
|
+
合辑作品数据: DouyinWorkParamsSchema,
|
|
622
|
+
评论数据: DouyinCommentParamsSchema,
|
|
623
|
+
用户主页数据: DouyinUserParamsSchema,
|
|
624
|
+
用户主页视频列表数据: DouyinUserParamsSchema,
|
|
625
|
+
热点词数据: DouyinHotWordsParamsSchema,
|
|
626
|
+
搜索数据: DouyinSearchParamsSchema,
|
|
627
|
+
音乐数据: DouyinMusicParamsSchema,
|
|
628
|
+
直播间信息数据: DouyinLiveRoomParamsSchema,
|
|
629
|
+
申请二维码数据: DouyinQrcodeParamsSchema,
|
|
630
|
+
Emoji数据: DouyinEmojiListParamsSchema,
|
|
631
|
+
动态表情数据: DouyinEmojiProParamsSchema,
|
|
632
|
+
指定评论回复数据: DouyinCommentReplyParamsSchema,
|
|
633
|
+
弹幕数据: DouyinDanmakuParamsSchema
|
|
670
634
|
};
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
getDynamicCard: createBoundBilibiliApiMethod("动态卡片数据", cookie),
|
|
689
|
-
getLiveRoomDetail: createBoundBilibiliApiMethod("直播间信息", cookie),
|
|
690
|
-
getLiveRoomInitInfo: createBoundBilibiliApiMethod("直播间初始化信息", cookie),
|
|
691
|
-
getLoginBasicInfo: createBoundBilibiliApiMethod("登录基本信息", cookie),
|
|
692
|
-
getLoginQrcode: createBoundBilibiliApiMethod("申请二维码", cookie),
|
|
693
|
-
checkQrcodeStatus: createBoundBilibiliApiMethod("二维码状态", cookie),
|
|
694
|
-
getUserTotalPlayCount: createBoundBilibiliApiMethod("获取UP主总播放量", cookie),
|
|
695
|
-
convertAvToBv: createBoundBilibiliApiMethod("AV转BV", cookie),
|
|
696
|
-
convertBvToAv: createBoundBilibiliApiMethod("BV转AV", cookie),
|
|
697
|
-
getArticleContent: createBoundBilibiliApiMethod("专栏正文内容", cookie),
|
|
698
|
-
getArticleCard: createBoundBilibiliApiMethod("专栏显示卡片信息", cookie),
|
|
699
|
-
getArticleInfo: createBoundBilibiliApiMethod("专栏文章基本信息", cookie),
|
|
700
|
-
getColumnInfo: createBoundBilibiliApiMethod("文集基本信息", cookie)
|
|
701
|
-
};
|
|
635
|
+
const DouyinMethodRoutes = {
|
|
636
|
+
聚合解析: "/fetch_one_work",
|
|
637
|
+
文字作品数据: "/fetch_one_work",
|
|
638
|
+
视频作品数据: "/fetch_one_work",
|
|
639
|
+
图集作品数据: "/fetch_one_work",
|
|
640
|
+
合辑作品数据: "/fetch_one_work",
|
|
641
|
+
评论数据: "/fetch_work_comments",
|
|
642
|
+
用户主页数据: "/fetch_user_info",
|
|
643
|
+
用户主页视频列表数据: "/fetch_user_post_videos",
|
|
644
|
+
搜索数据: "/fetch_search_info",
|
|
645
|
+
热点词数据: "/fetch_suggest_words",
|
|
646
|
+
音乐数据: "/fetch_music_work",
|
|
647
|
+
Emoji数据: "/fetch_emoji_list",
|
|
648
|
+
动态表情数据: "/fetch_emoji_pro_list",
|
|
649
|
+
直播间信息数据: "/fetch_user_live_videos",
|
|
650
|
+
指定评论回复数据: "/fetch_video_comment_replies",
|
|
651
|
+
弹幕数据: "/fetch_work_danmaku"
|
|
702
652
|
};
|
|
703
653
|
|
|
704
654
|
//#endregion
|
|
705
|
-
//#region src/
|
|
706
|
-
const
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
const
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
"V",
|
|
720
|
-
"1",
|
|
721
|
-
"0",
|
|
722
|
-
"0",
|
|
723
|
-
"0",
|
|
724
|
-
"0",
|
|
725
|
-
"0",
|
|
726
|
-
"0",
|
|
727
|
-
"0",
|
|
728
|
-
"0",
|
|
729
|
-
"0"
|
|
730
|
-
];
|
|
731
|
-
let bvIndex = bytes.length - 1;
|
|
732
|
-
let tmp = (MAX_AID | BigInt(aid)) ^ XOR_CODE;
|
|
733
|
-
while (tmp > 0) {
|
|
734
|
-
bytes[bvIndex] = data[Number(tmp % BigInt(BASE))];
|
|
735
|
-
tmp = tmp / BASE;
|
|
736
|
-
bvIndex -= 1;
|
|
737
|
-
}
|
|
738
|
-
[bytes[3], bytes[9]] = [bytes[9], bytes[3]];
|
|
739
|
-
[bytes[4], bytes[7]] = [bytes[7], bytes[4]];
|
|
740
|
-
return bytes.join("");
|
|
655
|
+
//#region src/validation/kuaishou.ts
|
|
656
|
+
const KuaishouVideoParamsSchema = zod.default.object({
|
|
657
|
+
methodType: zod.default.literal("单个视频作品数据", { error: "方法类型必须是\"单个视频作品数据\"" }),
|
|
658
|
+
photoId: zod.default.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" })
|
|
659
|
+
});
|
|
660
|
+
const KuaishouCommentParamsSchema = zod.default.object({
|
|
661
|
+
methodType: zod.default.literal("评论数据", { error: "方法类型必须是\"评论数据\"" }),
|
|
662
|
+
photoId: zod.default.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" })
|
|
663
|
+
});
|
|
664
|
+
const KuaishouEmojiParamsSchema = zod.default.object({ methodType: zod.default.literal("Emoji数据", { error: "方法类型必须是\"Emoji数据\"" }) });
|
|
665
|
+
const KuaishouValidationSchemas = {
|
|
666
|
+
单个视频作品数据: KuaishouVideoParamsSchema,
|
|
667
|
+
评论数据: KuaishouCommentParamsSchema,
|
|
668
|
+
Emoji数据: KuaishouEmojiParamsSchema
|
|
741
669
|
};
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
*/
|
|
747
|
-
const bv2av = (bvid) => {
|
|
748
|
-
const bvidArr = Array.from(bvid);
|
|
749
|
-
[bvidArr[3], bvidArr[9]] = [bvidArr[9], bvidArr[3]];
|
|
750
|
-
[bvidArr[4], bvidArr[7]] = [bvidArr[7], bvidArr[4]];
|
|
751
|
-
bvidArr.splice(0, 3);
|
|
752
|
-
const tmp = bvidArr.reduce((pre, bvidChar) => pre * BASE + BigInt(data.indexOf(bvidChar)), 0n);
|
|
753
|
-
return Number(tmp & MASK_CODE ^ XOR_CODE);
|
|
670
|
+
const KuaishouMethodRoutes = {
|
|
671
|
+
单个视频作品数据: "/fetch_one_work",
|
|
672
|
+
评论数据: "/fetch_work_comments",
|
|
673
|
+
Emoji数据: "/fetch_emoji_list"
|
|
754
674
|
};
|
|
755
675
|
|
|
756
676
|
//#endregion
|
|
757
|
-
//#region src/platform/
|
|
677
|
+
//#region src/platform/xiaohongshu/sign/index.ts
|
|
758
678
|
/**
|
|
759
|
-
*
|
|
679
|
+
* 小红书签名算法类
|
|
760
680
|
*/
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
*
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
*
|
|
835
|
-
* @
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
const value = params[key].toString().replace(chr_filter, "");
|
|
847
|
-
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
|
|
848
|
-
}).join("&");
|
|
849
|
-
return `&wts=${curr_time}&w_rid=${node_crypto.default.createHash("md5").update(query + mixin_key).digest("hex")}`;
|
|
850
|
-
};
|
|
851
|
-
/**
|
|
852
|
-
* 获取最新的 img_key 和 sub_key
|
|
853
|
-
* @param cookie - 有效的用户 Cookie 字符串
|
|
854
|
-
* @returns 返回包含 img_key 和 sub_key 的对象
|
|
855
|
-
* @throws 当网络请求失败或响应格式不正确时抛出错误
|
|
856
|
-
*/
|
|
857
|
-
const getWbiKeys = async (cookie) => {
|
|
858
|
-
const { data: { wbi_img: { img_url, sub_url } } } = (await (0, axios.default)("https://api.bilibili.com/x/web-interface/nav", { headers: { Cookie: cookie } })).data;
|
|
859
|
-
return {
|
|
860
|
-
img_key: img_url.slice(img_url.lastIndexOf("/") + 1, img_url.lastIndexOf(".")),
|
|
861
|
-
sub_key: sub_url.slice(sub_url.lastIndexOf("/") + 1, sub_url.lastIndexOf("."))
|
|
862
|
-
};
|
|
863
|
-
};
|
|
864
|
-
/**
|
|
865
|
-
* 对请求链接进行 WBI 签名
|
|
866
|
-
* @param BASEURL - 完整的请求地址,可以是字符串或 URL 对象
|
|
867
|
-
* @param cookie - 有效的用户 Cookie 字符串
|
|
868
|
-
* @returns 返回包含 WBI 签名的查询字符串
|
|
869
|
-
* @throws 当获取 WBI 密钥失败或 URL 解析失败时抛出错误
|
|
870
|
-
*/
|
|
871
|
-
const wbi_sign = async (BASEURL, cookie) => {
|
|
872
|
-
const web_keys = await getWbiKeys(cookie);
|
|
873
|
-
const url$1 = new URL(BASEURL);
|
|
874
|
-
const params = {};
|
|
875
|
-
for (const [key, value] of url$1.searchParams.entries()) params[key] = value;
|
|
876
|
-
return encWbi(params, web_keys.img_key, web_keys.sub_key);
|
|
681
|
+
var xiaohongshuSign = class {
|
|
682
|
+
static client = new __ikenxuan_xhshow_ts.Xhshow();
|
|
683
|
+
/**
|
|
684
|
+
* 生成GET请求的X-S签名
|
|
685
|
+
* @param path - API路径
|
|
686
|
+
* @param a1Cookie - a1 cookie值
|
|
687
|
+
* @param clientType - 客户端类型,默认为 'xhs-pc-web'
|
|
688
|
+
* @param params - 查询参数对象
|
|
689
|
+
* @returns X-S签名
|
|
690
|
+
*/
|
|
691
|
+
static generateXSGet(path$1, a1Cookie, clientType = "xhs-pc-web", params = {}) {
|
|
692
|
+
return this.client.signXsGet(path$1, a1Cookie, clientType, params);
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* 生成POST请求的X-S签名
|
|
696
|
+
* @param path - API路径
|
|
697
|
+
* @param a1Cookie - a1 cookie值
|
|
698
|
+
* @param clientType - 客户端类型,默认为 'xhs-pc-web'
|
|
699
|
+
* @param body - 请求体对象
|
|
700
|
+
* @returns X-S签名
|
|
701
|
+
*/
|
|
702
|
+
static generateXSPost(path$1, a1Cookie, clientType = "xhs-pc-web", body = {}) {
|
|
703
|
+
return this.client.signXsPost(path$1, a1Cookie, clientType, body);
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* 生成X-S签名(兼容旧接口)
|
|
707
|
+
* @param url - 请求URL
|
|
708
|
+
* @param body - 请求体
|
|
709
|
+
* @param userAgent - User-Agent(暂未使用)
|
|
710
|
+
* @param method - 请求方法,默认为 'POST'
|
|
711
|
+
* @param a1Cookie - a1 cookie值
|
|
712
|
+
* @returns X-S签名
|
|
713
|
+
*/
|
|
714
|
+
static generateXS(url$1, body, userAgent, method = "POST", a1Cookie = "") {
|
|
715
|
+
try {
|
|
716
|
+
const urlObj = new URL(url$1);
|
|
717
|
+
const path$1 = urlObj.pathname + urlObj.search;
|
|
718
|
+
if (method.toUpperCase() === "GET") {
|
|
719
|
+
const params = typeof body === "object" ? body : {};
|
|
720
|
+
return this.generateXSGet(path$1, a1Cookie, "xhs-pc-web", params);
|
|
721
|
+
} else {
|
|
722
|
+
const requestBody = typeof body === "object" ? body : {};
|
|
723
|
+
return this.generateXSPost(path$1, a1Cookie, "xhs-pc-web", requestBody);
|
|
724
|
+
}
|
|
725
|
+
} catch (error) {
|
|
726
|
+
console.error("生成X-S签名失败:", error);
|
|
727
|
+
throw new Error(`签名生成失败: ${error}`);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* 生成X-S-Common参数
|
|
732
|
+
* @param length - 长度
|
|
733
|
+
* @returns Base64编码的随机字符串
|
|
734
|
+
*/
|
|
735
|
+
static generateXSCommon(length = 945) {
|
|
736
|
+
return node_crypto.default.randomBytes(length).toString("base64").replace(/=+$/, "");
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* 生成X-T时间戳
|
|
740
|
+
* @returns 当前时间戳字符串
|
|
741
|
+
*/
|
|
742
|
+
static generateXT() {
|
|
743
|
+
return Date.now().toString();
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* 生成X-B3-Traceid
|
|
747
|
+
* @returns 16位随机字符串
|
|
748
|
+
*/
|
|
749
|
+
static generateXB3Traceid() {
|
|
750
|
+
return Array.from({ length: 16 }, () => "abcdef0123456789"[Math.floor(Math.random() * 16)]).join("");
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* 从cookie字符串中提取a1值
|
|
754
|
+
* @param cookieString - 完整的cookie字符串
|
|
755
|
+
* @returns a1 cookie值
|
|
756
|
+
*/
|
|
757
|
+
static extractA1FromCookie(cookieString) {
|
|
758
|
+
const match = cookieString.match(/a1=([^;]+)/);
|
|
759
|
+
return match ? match[1] : "";
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* 生成搜索ID
|
|
763
|
+
* @returns 搜索ID字符串
|
|
764
|
+
*/
|
|
765
|
+
static getSearchId = () => (BigInt(Date.now()) << 64n) + BigInt(Math.floor(Math.random() * 2147483646)).toString(36);
|
|
877
766
|
};
|
|
878
767
|
|
|
879
768
|
//#endregion
|
|
880
|
-
//#region src/
|
|
769
|
+
//#region src/platform/xiaohongshu/API.ts
|
|
881
770
|
/**
|
|
882
|
-
*
|
|
771
|
+
* 搜索排序类型枚举
|
|
883
772
|
*/
|
|
884
|
-
|
|
885
|
-
code;
|
|
886
|
-
platform;
|
|
773
|
+
let SearchSortType = /* @__PURE__ */ function(SearchSortType$1) {
|
|
887
774
|
/**
|
|
888
|
-
*
|
|
889
|
-
* @param message - 错误消息
|
|
890
|
-
* @param code - 错误代码
|
|
891
|
-
* @param platform - 平台名称
|
|
775
|
+
* 默认排序
|
|
892
776
|
*/
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
777
|
+
SearchSortType$1["GENERAL"] = "general";
|
|
778
|
+
/**
|
|
779
|
+
* 最受欢迎(按热度降序)
|
|
780
|
+
*/
|
|
781
|
+
SearchSortType$1["MOST_POPULAR"] = "popularity_descending";
|
|
782
|
+
/**
|
|
783
|
+
* 最新发布(按时间降序)
|
|
784
|
+
*/
|
|
785
|
+
SearchSortType$1["LATEST"] = "time_descending";
|
|
786
|
+
return SearchSortType$1;
|
|
787
|
+
}({});
|
|
900
788
|
/**
|
|
901
|
-
*
|
|
789
|
+
* 搜索笔记类型枚举
|
|
902
790
|
*/
|
|
903
|
-
|
|
904
|
-
errors;
|
|
905
|
-
requestPath;
|
|
791
|
+
let SearchNoteType = /* @__PURE__ */ function(SearchNoteType$1) {
|
|
906
792
|
/**
|
|
907
|
-
*
|
|
908
|
-
* @param message - 错误消息
|
|
909
|
-
* @param errors - 详细错误信息
|
|
910
|
-
* @param requestPath - HTTP请求路径
|
|
793
|
+
* 默认(全部类型)
|
|
911
794
|
*/
|
|
912
|
-
|
|
913
|
-
super(message);
|
|
914
|
-
this.name = "ValidationError";
|
|
915
|
-
this.errors = errors;
|
|
916
|
-
this.requestPath = requestPath;
|
|
917
|
-
}
|
|
795
|
+
SearchNoteType$1[SearchNoteType$1["ALL"] = 0] = "ALL";
|
|
918
796
|
/**
|
|
919
|
-
*
|
|
920
|
-
* @param zodError - Zod验证错误
|
|
921
|
-
* @param requestPath - HTTP请求路径
|
|
922
|
-
* @returns 验证错误实例
|
|
797
|
+
* 仅视频
|
|
923
798
|
*/
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
799
|
+
SearchNoteType$1[SearchNoteType$1["VIDEO"] = 1] = "VIDEO";
|
|
800
|
+
/**
|
|
801
|
+
* 仅图片
|
|
802
|
+
*/
|
|
803
|
+
SearchNoteType$1[SearchNoteType$1["IMAGE"] = 2] = "IMAGE";
|
|
804
|
+
return SearchNoteType$1;
|
|
805
|
+
}({});
|
|
806
|
+
/**
|
|
807
|
+
* 构建查询字符串
|
|
808
|
+
* @param params - 参数对象
|
|
809
|
+
* @returns 查询字符串
|
|
810
|
+
*/
|
|
811
|
+
const buildQueryString$1 = (params) => {
|
|
812
|
+
return Object.entries(params).filter(([_, value]) => value !== void 0 && value !== null).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
|
|
930
813
|
};
|
|
931
814
|
/**
|
|
932
|
-
*
|
|
933
|
-
* @param error - 错误对象
|
|
934
|
-
* @param requestPath - HTTP请求路径(可选)
|
|
935
|
-
* @returns 统一的错误响应格式
|
|
815
|
+
* 小红书API地址配置
|
|
936
816
|
*/
|
|
937
|
-
const
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
8,
|
|
1044
|
-
9,
|
|
1045
|
-
10,
|
|
1046
|
-
11,
|
|
1047
|
-
12,
|
|
1048
|
-
13,
|
|
1049
|
-
14,
|
|
1050
|
-
15,
|
|
1051
|
-
16,
|
|
1052
|
-
17,
|
|
1053
|
-
18,
|
|
1054
|
-
19,
|
|
1055
|
-
20,
|
|
1056
|
-
21,
|
|
1057
|
-
22,
|
|
1058
|
-
33
|
|
1059
|
-
].includes(val), { error: "无效的评论区类型" }),
|
|
1060
|
-
root: zod.default.string({ error: "根评论ID必须是字符串" }).min(1, { error: "根评论ID不能为空" }),
|
|
1061
|
-
number: zod.default.coerce.number({ error: "评论数量必须是数字" }).int({ error: "评论数量必须是整数" }).positive({ error: "评论数量必须是正数" }).default(20).optional(),
|
|
1062
|
-
pn: zod.default.coerce.number({ error: "页码必须是数字" }).int({ error: "页码必须是整数" }).positive({ error: "页码必须是正数" }).default(1).optional()
|
|
1063
|
-
});
|
|
1064
|
-
const BilibiliUserParamsSchema = zod.default.object({
|
|
1065
|
-
methodType: zod.default.enum([
|
|
1066
|
-
"用户主页数据",
|
|
1067
|
-
"用户主页动态列表数据",
|
|
1068
|
-
"获取UP主总播放量"
|
|
1069
|
-
], { error: "方法类型必须是指定的枚举值之一" }),
|
|
1070
|
-
host_mid: smartNumber("UP主UID不能为空", 1, true)
|
|
1071
|
-
});
|
|
1072
|
-
const BilibiliEmojiParamsSchema = zod.default.object({ methodType: zod.default.literal("Emoji数据", { error: "方法类型必须是\"Emoji数据\"" }) });
|
|
1073
|
-
const BilibiliBangumiInfoParamsSchema = zod.default.object({
|
|
1074
|
-
methodType: zod.default.literal("番剧基本信息数据", { error: "方法类型必须是\"番剧基本信息数据\"" }),
|
|
1075
|
-
ep_id: zod.default.string({ error: "番剧EP ID必须是字符串" }).min(1, { error: "番剧EP ID不能为空" }).optional(),
|
|
1076
|
-
season_id: zod.default.string({ error: "番剧季度ID必须是字符串" }).optional()
|
|
1077
|
-
}).refine((data$1) => data$1.ep_id ?? data$1.season_id, {
|
|
1078
|
-
error: "ep_id 和 season_id 至少需要提供一个",
|
|
1079
|
-
path: ["ep_id"]
|
|
1080
|
-
});
|
|
1081
|
-
const BilibiliBangumiStreamParamsSchema = zod.default.object({
|
|
1082
|
-
methodType: zod.default.literal("番剧下载信息数据", { error: "方法类型必须是\"番剧下载信息数据\"" }),
|
|
1083
|
-
cid: smartNumber("CID不能为空", 1, true),
|
|
1084
|
-
ep_id: zod.default.string({ error: "番剧EP ID必须是字符串" }).min(1, { error: "番剧EP ID不能为空" })
|
|
1085
|
-
});
|
|
1086
|
-
const BilibiliDynamicParamsSchema = zod.default.object({
|
|
1087
|
-
methodType: zod.default.enum(["动态详情数据", "动态卡片数据"], { error: "方法类型必须是\"动态详情数据\"或\"动态卡片数据\"" }),
|
|
1088
|
-
dynamic_id: zod.default.string({ error: "动态ID必须是字符串" }).min(1, { error: "动态ID不能为空" })
|
|
1089
|
-
});
|
|
1090
|
-
const BilibiliLiveParamsSchema = zod.default.object({
|
|
1091
|
-
methodType: zod.default.enum(["直播间信息", "直播间初始化信息"], { error: "方法类型必须是\"直播间信息\"或\"直播间初始化信息\"" }),
|
|
1092
|
-
room_id: zod.default.string({ error: "直播间ID必须是字符串" }).min(1, { error: "直播间ID不能为空" })
|
|
1093
|
-
});
|
|
1094
|
-
const BilibiliLoginParamsSchema = zod.default.object({ methodType: zod.default.literal("登录基本信息", { error: "方法类型必须是\"登录基本信息\"" }) });
|
|
1095
|
-
const BilibiliQrcodeParamsSchema = zod.default.object({ methodType: zod.default.literal("申请二维码", { error: "方法类型必须是\"申请二维码\"" }) });
|
|
1096
|
-
const BilibiliQrcodeStatusParamsSchema = zod.default.object({
|
|
1097
|
-
methodType: zod.default.literal("二维码状态", { error: "方法类型必须是\"二维码状态\"" }),
|
|
1098
|
-
qrcode_key: zod.default.string({ error: "二维码key必须是字符串" }).min(1, { error: "二维码key不能为空" })
|
|
1099
|
-
});
|
|
1100
|
-
const BilibiliAv2BvParamsSchema = zod.default.object({
|
|
1101
|
-
methodType: zod.default.literal("AV转BV", { error: "方法类型必须是\"AV转BV\"" }),
|
|
1102
|
-
avid: zod.default.coerce.number({ error: "AVID必须是数字" }).int({ error: "AVID必须是整数" }).positive({ error: "AVID必须是正数" })
|
|
1103
|
-
});
|
|
1104
|
-
const BilibiliBv2AvParamsSchema = zod.default.object({
|
|
1105
|
-
methodType: zod.default.literal("BV转AV", { error: "方法类型必须是\"BV转AV\"" }),
|
|
1106
|
-
bvid: zod.default.string({ error: "BVID必须是字符串" }).min(1, { error: "BVID不能为空" })
|
|
1107
|
-
});
|
|
1108
|
-
const BilibiliArticleParamsSchema = zod.default.object({
|
|
1109
|
-
methodType: zod.default.literal("专栏正文内容", { error: "方法类型必须是\"专栏正文内容\"" }),
|
|
1110
|
-
id: zod.default.string({ error: "专栏ID必须是字符串" }).min(1, { error: "专栏ID不能为空" })
|
|
1111
|
-
});
|
|
1112
|
-
const BilibiliArticleCardParamsSchema = zod.default.object({
|
|
1113
|
-
methodType: zod.default.literal("专栏显示卡片信息", { error: "方法类型必须是\"专栏显示卡片信息\"" }),
|
|
1114
|
-
ids: zod.default.union([zod.default.array(zod.default.string({ error: "被查询的 id 列表必须是字符串数组" })).min(1, { error: "被查询的 id 列表不能为空" }), zod.default.string({ error: "被查询的 id 列表必须是字符串" }).min(1, { error: "被查询的 id 列表不能为空" })])
|
|
1115
|
-
});
|
|
1116
|
-
const BilibiliArticleInfoParamsSchema = zod.default.object({
|
|
1117
|
-
methodType: zod.default.literal("专栏文章基本信息", { error: "方法类型必须是\"专栏文章基本信息\"" }),
|
|
1118
|
-
id: zod.default.string({ error: "专栏ID必须是字符串" }).min(1, { error: "专栏ID不能为空" })
|
|
1119
|
-
});
|
|
1120
|
-
const BilibiliColumnInfoParamsSchema = zod.default.object({
|
|
1121
|
-
methodType: zod.default.literal("文集基本信息", { error: "方法类型必须是\"文集基本信息\"" }),
|
|
1122
|
-
id: zod.default.string({ error: "文集ID必须是字符串" }).min(1, { error: "文集ID不能为空" })
|
|
1123
|
-
});
|
|
1124
|
-
const BilibiliValidationSchemas = {
|
|
1125
|
-
单个视频作品数据: BilibiliVideoParamsSchema,
|
|
1126
|
-
单个视频下载信息数据: BilibiliVideoDownloadParamsSchema,
|
|
1127
|
-
评论数据: BilibiliCommentParamsSchema,
|
|
1128
|
-
指定评论的回复: BilibiliCommentReplyParamsSchema,
|
|
1129
|
-
用户主页数据: BilibiliUserParamsSchema,
|
|
1130
|
-
用户主页动态列表数据: BilibiliUserParamsSchema,
|
|
1131
|
-
Emoji数据: BilibiliEmojiParamsSchema,
|
|
1132
|
-
番剧基本信息数据: BilibiliBangumiInfoParamsSchema,
|
|
1133
|
-
番剧下载信息数据: BilibiliBangumiStreamParamsSchema,
|
|
1134
|
-
动态详情数据: BilibiliDynamicParamsSchema,
|
|
1135
|
-
动态卡片数据: BilibiliDynamicParamsSchema,
|
|
1136
|
-
直播间信息: BilibiliLiveParamsSchema,
|
|
1137
|
-
直播间初始化信息: BilibiliLiveParamsSchema,
|
|
1138
|
-
登录基本信息: BilibiliLoginParamsSchema,
|
|
1139
|
-
申请二维码: BilibiliQrcodeParamsSchema,
|
|
1140
|
-
二维码状态: BilibiliQrcodeStatusParamsSchema,
|
|
1141
|
-
获取UP主总播放量: BilibiliUserParamsSchema,
|
|
1142
|
-
AV转BV: BilibiliAv2BvParamsSchema,
|
|
1143
|
-
BV转AV: BilibiliBv2AvParamsSchema,
|
|
1144
|
-
专栏正文内容: BilibiliArticleParamsSchema,
|
|
1145
|
-
专栏显示卡片信息: BilibiliArticleCardParamsSchema,
|
|
1146
|
-
专栏文章基本信息: BilibiliArticleInfoParamsSchema,
|
|
1147
|
-
文集基本信息: BilibiliColumnInfoParamsSchema
|
|
1148
|
-
};
|
|
1149
|
-
const BilibiliMethodRoutes = {
|
|
1150
|
-
单个视频作品数据: "/fetch_one_video",
|
|
1151
|
-
单个视频下载信息数据: "/fetch_video_playurl",
|
|
1152
|
-
评论数据: "/fetch_work_comments",
|
|
1153
|
-
指定评论的回复: "/fetch_comment_reply",
|
|
1154
|
-
用户主页数据: "/fetch_user_profile",
|
|
1155
|
-
用户主页动态列表数据: "/fetch_user_dynamic",
|
|
1156
|
-
Emoji数据: "/fetch_emoji_list",
|
|
1157
|
-
番剧基本信息数据: "/fetch_bangumi_video_info",
|
|
1158
|
-
番剧下载信息数据: "/fetch_bangumi_video_playurl",
|
|
1159
|
-
动态详情数据: "/fetch_dynamic_info",
|
|
1160
|
-
动态卡片数据: "/fetch_dynamic_card",
|
|
1161
|
-
直播间信息: "/fetch_live_room_detail",
|
|
1162
|
-
直播间初始化信息: "/fetch_liveroom_def",
|
|
1163
|
-
登录基本信息: "/login_basic_info",
|
|
1164
|
-
申请二维码: "/new_login_qrcode",
|
|
1165
|
-
二维码状态: "/check_qrcode",
|
|
1166
|
-
获取UP主总播放量: "/fetch_user_full_view",
|
|
1167
|
-
AV转BV: "/av_to_bv",
|
|
1168
|
-
BV转AV: "/bv_to_av",
|
|
1169
|
-
专栏正文内容: "/fetch_article_content",
|
|
1170
|
-
专栏显示卡片信息: "/fetch_article_card",
|
|
1171
|
-
专栏文章基本信息: "/fetch_article_info",
|
|
1172
|
-
文集基本信息: "/fetch_column_info"
|
|
817
|
+
const xiaohongshuApiUrls = {
|
|
818
|
+
首页推荐数据(data$1 = {}) {
|
|
819
|
+
return {
|
|
820
|
+
apiPath: "/api/sns/web/v1/homefeed",
|
|
821
|
+
Url: "https://edith.xiaohongshu.com/api/sns/web/v1/homefeed",
|
|
822
|
+
Body: {
|
|
823
|
+
cursor_score: data$1.cursor_score ?? "1.7599348899670024E9",
|
|
824
|
+
num: data$1.num ?? 33,
|
|
825
|
+
refresh_type: data$1.refresh_type ?? 3,
|
|
826
|
+
note_index: data$1.note_index ?? 33,
|
|
827
|
+
category: data$1.category ?? "homefeed_recommend",
|
|
828
|
+
search_key: data$1.search_key ?? "",
|
|
829
|
+
image_formats: [
|
|
830
|
+
"jpg",
|
|
831
|
+
"webp",
|
|
832
|
+
"avif"
|
|
833
|
+
]
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
},
|
|
837
|
+
单个笔记数据(data$1) {
|
|
838
|
+
return {
|
|
839
|
+
apiPath: "/api/sns/web/v1/feed",
|
|
840
|
+
Url: "https://edith.xiaohongshu.com/api/sns/web/v1/feed",
|
|
841
|
+
Body: {
|
|
842
|
+
source_note_id: data$1.note_id,
|
|
843
|
+
image_formats: [
|
|
844
|
+
"jpg",
|
|
845
|
+
"webp",
|
|
846
|
+
"avif"
|
|
847
|
+
],
|
|
848
|
+
extra: { need_body_topic: "1" },
|
|
849
|
+
xsec_source: "pc_feed",
|
|
850
|
+
xsec_token: data$1.xsec_token
|
|
851
|
+
}
|
|
852
|
+
};
|
|
853
|
+
},
|
|
854
|
+
评论数据(data$1) {
|
|
855
|
+
return {
|
|
856
|
+
apiPath: "/api/sns/web/v2/comment/page",
|
|
857
|
+
Url: `https://edith.xiaohongshu.com/api/sns/web/v2/comment/page?${buildQueryString$1({
|
|
858
|
+
note_id: data$1.note_id,
|
|
859
|
+
cursor: data$1.cursor ?? "",
|
|
860
|
+
image_formats: [
|
|
861
|
+
"jpg",
|
|
862
|
+
"webp",
|
|
863
|
+
"avif"
|
|
864
|
+
].join(","),
|
|
865
|
+
xsec_token: data$1.xsec_token
|
|
866
|
+
})}`
|
|
867
|
+
};
|
|
868
|
+
},
|
|
869
|
+
用户数据(data$1) {
|
|
870
|
+
return {
|
|
871
|
+
apiPath: "/api/sns/web/v1/user/otherinfo",
|
|
872
|
+
Url: `https://www.xiaohongshu.com/user/profile/${data$1.user_id}`
|
|
873
|
+
};
|
|
874
|
+
},
|
|
875
|
+
用户笔记数据(data$1) {
|
|
876
|
+
return {
|
|
877
|
+
apiPath: "/api/sns/web/v1/user_posted",
|
|
878
|
+
Url: `https://edith.xiaohongshu.com/api/sns/web/v1/user_posted?${buildQueryString$1({
|
|
879
|
+
user_id: data$1.user_id,
|
|
880
|
+
cursor: data$1.cursor ?? "",
|
|
881
|
+
num: data$1.num ?? 30,
|
|
882
|
+
image_formats: [
|
|
883
|
+
"jpg",
|
|
884
|
+
"webp",
|
|
885
|
+
"avif"
|
|
886
|
+
].join(","),
|
|
887
|
+
xsec_source: "pc_feed"
|
|
888
|
+
})}`
|
|
889
|
+
};
|
|
890
|
+
},
|
|
891
|
+
表情列表(data$1) {
|
|
892
|
+
return {
|
|
893
|
+
apiPath: "/api/im/redmoji/detail",
|
|
894
|
+
Url: "https://edith.xiaohongshu.com/api/im/redmoji/detail"
|
|
895
|
+
};
|
|
896
|
+
},
|
|
897
|
+
搜索笔记(data$1) {
|
|
898
|
+
return {
|
|
899
|
+
apiPath: "/api/sns/web/v1/search/notes",
|
|
900
|
+
Body: {
|
|
901
|
+
keyword: data$1.keyword,
|
|
902
|
+
page: data$1.page ?? 1,
|
|
903
|
+
page_size: data$1.page_size ?? 20,
|
|
904
|
+
sort: SearchSortType.GENERAL,
|
|
905
|
+
note_type: SearchNoteType.ALL,
|
|
906
|
+
search_id: xiaohongshuSign.getSearchId(),
|
|
907
|
+
image_formats: [
|
|
908
|
+
"jpg",
|
|
909
|
+
"webp",
|
|
910
|
+
"avif"
|
|
911
|
+
]
|
|
912
|
+
},
|
|
913
|
+
Url: "https://edith.xiaohongshu.com/api/sns/web/v1/search/notes"
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
};
|
|
917
|
+
/**
|
|
918
|
+
* 创建小红书API URLs实例
|
|
919
|
+
* @returns 小红书API URLs对象
|
|
920
|
+
*/
|
|
921
|
+
const createXiaohongshuApiUrls = () => {
|
|
922
|
+
return xiaohongshuApiUrls;
|
|
1173
923
|
};
|
|
1174
924
|
|
|
1175
925
|
//#endregion
|
|
1176
|
-
//#region src/validation/
|
|
1177
|
-
const
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
})
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
cursor: zod.default.coerce.number({ error: "游标必须是数字" }).int({ error: "游标必须是整数" }).min(0, { error: "游标不能小于0" }).default(0).optional()
|
|
1191
|
-
});
|
|
1192
|
-
const DouyinHotWordsParamsSchema = zod.default.object({
|
|
1193
|
-
methodType: zod.default.literal("热点词数据", { error: "方法类型必须是\"热点词数据\"" }),
|
|
1194
|
-
query: zod.default.string({ error: "搜索词必须是字符串" }).min(1, { error: "搜索词不能为空" })
|
|
1195
|
-
});
|
|
1196
|
-
const DouyinSearchParamsSchema = zod.default.object({
|
|
1197
|
-
methodType: zod.default.literal("搜索数据", { error: "方法类型必须是\"搜索数据\"" }),
|
|
1198
|
-
query: zod.default.string({ error: "搜索词必须是字符串" }).min(1, { error: "搜索词不能为空" }),
|
|
1199
|
-
type: zod.default.enum([
|
|
1200
|
-
"综合",
|
|
1201
|
-
"用户",
|
|
1202
|
-
"视频"
|
|
1203
|
-
], { error: "搜索类型必须是\"综合\"、\"用户\"或\"视频\"" }).optional().default("综合"),
|
|
1204
|
-
number: smartPositiveInteger("搜索数量必须是正整数").optional().default(10),
|
|
1205
|
-
search_id: zod.default.string({ error: "搜索ID必须是字符串" }).optional()
|
|
1206
|
-
});
|
|
1207
|
-
const DouyinCommentReplyParamsSchema = zod.default.object({
|
|
1208
|
-
methodType: zod.default.literal("指定评论回复数据", { error: "方法类型必须是\"指定评论回复数据\"" }),
|
|
1209
|
-
aweme_id: zod.default.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" }),
|
|
1210
|
-
comment_id: zod.default.string({ error: "评论ID必须z串" }).min(1, { error: "评论ID不能为空" }),
|
|
1211
|
-
number: smartPositiveInteger("评论数量必须是正整数").optional().default(5),
|
|
1212
|
-
cursor: zod.default.coerce.number({ error: "游标必须是数字" }).int({ error: "游标必须是整数" }).min(0, { error: "游标不能小于0" }).default(0).optional()
|
|
926
|
+
//#region src/validation/xiaohongshu.ts
|
|
927
|
+
const SearchSortTypeValues = Object.values(SearchSortType).filter((v) => typeof v === "string");
|
|
928
|
+
const SearchNoteTypeValues = Object.values(SearchNoteType).filter((v) => typeof v === "number");
|
|
929
|
+
/**
|
|
930
|
+
* 小红书首页推荐数据参数验证模式
|
|
931
|
+
*/
|
|
932
|
+
const HomeFeedParamsSchema = zod.default.object({
|
|
933
|
+
methodType: zod.default.literal("首页推荐数据", { error: "方法类型必须是\"首页推荐数据\"" }),
|
|
934
|
+
cursor_score: zod.default.string({ error: "cursor_score必须是字符串" }).optional(),
|
|
935
|
+
num: zod.default.coerce.number({ error: "数量必须是数字" }).int({ error: "数量必须是整数" }).min(1, { error: "数量不能小于1" }).max(100, { error: "数量不能大于100" }).optional(),
|
|
936
|
+
refresh_type: zod.default.coerce.number({ error: "refresh_type必须是数字" }).int({ error: "refresh_type必须是整数" }).optional(),
|
|
937
|
+
note_index: zod.default.coerce.number({ error: "note_index必须是数字" }).int({ error: "note_index必须是整数" }).optional(),
|
|
938
|
+
category: zod.default.string({ error: "category必须是字符串" }).optional(),
|
|
939
|
+
search_key: zod.default.string({ error: "search_key必须是字符串" }).optional()
|
|
1213
940
|
});
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
941
|
+
/**
|
|
942
|
+
* 小红书单个笔记数据参数验证模式
|
|
943
|
+
*/
|
|
944
|
+
const NoteParamsSchema = zod.default.object({
|
|
945
|
+
methodType: zod.default.literal("单个笔记数据", { error: "方法类型必须是\"单个笔记数据\"" }),
|
|
946
|
+
note_id: zod.default.string({ error: "note_id必须是字符串" }),
|
|
947
|
+
xsec_token: zod.default.string({ error: "xsec_token必须是字符串" })
|
|
1217
948
|
});
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
949
|
+
/**
|
|
950
|
+
* 小红书评论数据参数验证模式
|
|
951
|
+
*/
|
|
952
|
+
const CommentParamsSchema = zod.default.object({
|
|
953
|
+
methodType: zod.default.literal("评论数据", { error: "方法类型必须是\"评论数据\"" }),
|
|
954
|
+
note_id: zod.default.string({ error: "note_id必须是字符串" }),
|
|
955
|
+
cursor: zod.default.string({ error: "cursor必须是字符串" }).optional(),
|
|
956
|
+
xsec_token: zod.default.string({ error: "xsec_token必须是字符串" })
|
|
1221
957
|
});
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
958
|
+
/**
|
|
959
|
+
* 小红书用户数据参数验证模式
|
|
960
|
+
*/
|
|
961
|
+
const UserParamsSchema = zod.default.object({
|
|
962
|
+
methodType: zod.default.literal("用户数据", { error: "方法类型必须是\"用户数据\"" }),
|
|
963
|
+
user_id: zod.default.string({ error: "user_id必须是字符串" })
|
|
1226
964
|
});
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
965
|
+
/**
|
|
966
|
+
* 小红书用户笔记数据参数验证模式
|
|
967
|
+
*/
|
|
968
|
+
const UserNoteParamsSchema = zod.default.object({
|
|
969
|
+
methodType: zod.default.literal("用户笔记数据", { error: "方法类型必须是\"用户笔记数据\"" }),
|
|
970
|
+
user_id: zod.default.string({ error: "user_id必须是字符串" }),
|
|
971
|
+
cursor: zod.default.string({ error: "cursor必须是字符串" }).optional(),
|
|
972
|
+
num: zod.default.coerce.number({ error: "数量必须是数字" }).int({ error: "数量必须是整数" }).min(1, { error: "数量不能小于1" }).max(100, { error: "数量不能大于100" }).optional()
|
|
1230
973
|
});
|
|
1231
|
-
const
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
}).
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
}, {
|
|
1243
|
-
error: "获取弹幕区间的结束时间不能超过视频总时长",
|
|
1244
|
-
path: ["end_time"]
|
|
1245
|
-
}).refine((data$1) => {
|
|
1246
|
-
if (data$1.start_time !== void 0 && data$1.end_time !== void 0) return data$1.start_time < data$1.end_time;
|
|
1247
|
-
return true;
|
|
1248
|
-
}, {
|
|
1249
|
-
error: "获取弹幕区间的开始时间必须小于结束时间",
|
|
1250
|
-
path: ["start_time"]
|
|
974
|
+
const EmojiListParamsSchema = zod.default.object({ methodType: zod.default.literal("表情列表", { error: "方法类型必须是\"表情列表\"" }) });
|
|
975
|
+
/**
|
|
976
|
+
* 小红书搜索笔记参数验证模式
|
|
977
|
+
*/
|
|
978
|
+
const SearchNoteParamsSchema = zod.default.object({
|
|
979
|
+
methodType: zod.default.literal("搜索笔记", { error: "方法类型必须是\"搜索笔记\"" }),
|
|
980
|
+
keyword: zod.default.string({ error: "keyword必须是字符串" }),
|
|
981
|
+
page: zod.default.coerce.number({ error: "page必须是数字" }).int({ error: "page必须是整数" }).min(1, { error: "page不能小于1" }).optional(),
|
|
982
|
+
page_size: zod.default.coerce.number({ error: "page_size必须是数字" }).int({ error: "page_size必须是整数" }).min(1, { error: "page_size不能小于1" }).max(100, { error: "page_size不能大于100" }).optional(),
|
|
983
|
+
sort: zod.default.enum(SearchSortTypeValues, { error: "排序类型不合法" }).optional(),
|
|
984
|
+
note_type: zod.default.coerce.number({ error: "笔记类型必须是数字" }).int({ error: "笔记类型必须是整数" }).refine((val) => SearchNoteTypeValues.includes(val), { message: "笔记类型不合法" }).optional()
|
|
1251
985
|
});
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
评论数据:
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
986
|
+
/**
|
|
987
|
+
* 小红书验证模式映射
|
|
988
|
+
*/
|
|
989
|
+
const XiaohongshuValidationSchemas = {
|
|
990
|
+
首页推荐数据: HomeFeedParamsSchema,
|
|
991
|
+
单个笔记数据: NoteParamsSchema,
|
|
992
|
+
评论数据: CommentParamsSchema,
|
|
993
|
+
用户数据: UserParamsSchema,
|
|
994
|
+
用户笔记数据: UserNoteParamsSchema,
|
|
995
|
+
表情列表: EmojiListParamsSchema,
|
|
996
|
+
搜索笔记: SearchNoteParamsSchema
|
|
997
|
+
};
|
|
998
|
+
/**
|
|
999
|
+
* 小红书方法类型
|
|
1000
|
+
*/
|
|
1001
|
+
const XiaohongshuMethodRoutes = {
|
|
1002
|
+
首页推荐数据: "/fetch_home_feed",
|
|
1003
|
+
单个笔记数据: "/fetch_one_note",
|
|
1004
|
+
评论数据: "/fetch_note_comments",
|
|
1005
|
+
用户数据: "/fetch_user_profile",
|
|
1006
|
+
用户笔记数据: "/fetch_user_notes",
|
|
1007
|
+
表情列表: "/fetch_emoji_list",
|
|
1008
|
+
搜索笔记: "/fetch_search_notes"
|
|
1009
|
+
};
|
|
1010
|
+
|
|
1011
|
+
//#endregion
|
|
1012
|
+
//#region src/validation/index.ts
|
|
1013
|
+
/**
|
|
1014
|
+
* 验证抖音参数
|
|
1015
|
+
* @param methodType - 抖音方法类型
|
|
1016
|
+
* @param params - 待验证的参数
|
|
1017
|
+
* @returns 验证后的参数,符合原始API期望的类型
|
|
1018
|
+
*/
|
|
1019
|
+
const validateDouyinParams = (methodType, params) => {
|
|
1020
|
+
return DouyinValidationSchemas[methodType].parse(typeof params === "object" && params !== null ? {
|
|
1021
|
+
methodType,
|
|
1022
|
+
...params
|
|
1023
|
+
} : {
|
|
1024
|
+
methodType,
|
|
1025
|
+
params
|
|
1026
|
+
});
|
|
1027
|
+
};
|
|
1028
|
+
/**
|
|
1029
|
+
* 验证哔哩哔哩参数
|
|
1030
|
+
* @param methodType - 哔哩哔哩方法类型
|
|
1031
|
+
* @param params - 待验证的参数
|
|
1032
|
+
* @returns 验证后的参数,符合原始API期望的类型
|
|
1033
|
+
*/
|
|
1034
|
+
const validateBilibiliParams = (methodType, params) => {
|
|
1035
|
+
return BilibiliValidationSchemas[methodType].parse(typeof params === "object" && params !== null ? {
|
|
1036
|
+
methodType,
|
|
1037
|
+
...params
|
|
1038
|
+
} : {
|
|
1039
|
+
methodType,
|
|
1040
|
+
params
|
|
1041
|
+
});
|
|
1042
|
+
};
|
|
1043
|
+
/**
|
|
1044
|
+
* 验证快手参数
|
|
1045
|
+
* @param methodType - 快手方法类型
|
|
1046
|
+
* @param params - 待验证的参数
|
|
1047
|
+
* @returns 验证后的参数,符合原始API期望的类型
|
|
1048
|
+
*/
|
|
1049
|
+
const validateKuaishouParams = (methodType, params) => {
|
|
1050
|
+
return KuaishouValidationSchemas[methodType].parse(typeof params === "object" && params !== null ? {
|
|
1051
|
+
methodType,
|
|
1052
|
+
...params
|
|
1053
|
+
} : {
|
|
1054
|
+
methodType,
|
|
1055
|
+
params
|
|
1056
|
+
});
|
|
1057
|
+
};
|
|
1058
|
+
/**
|
|
1059
|
+
* 验证小红书参数
|
|
1060
|
+
* @param methodType - 小红书方法类型
|
|
1061
|
+
* @param params - 待验证的参数
|
|
1062
|
+
* @returns 验证后的参数
|
|
1063
|
+
*/
|
|
1064
|
+
const validateXiaohongshuParams = (methodType, params) => {
|
|
1065
|
+
return XiaohongshuValidationSchemas[methodType].parse(typeof params === "object" && params !== null ? {
|
|
1066
|
+
methodType,
|
|
1067
|
+
...params
|
|
1068
|
+
} : {
|
|
1069
|
+
methodType,
|
|
1070
|
+
params
|
|
1071
|
+
});
|
|
1072
|
+
};
|
|
1073
|
+
/**
|
|
1074
|
+
* 创建成功响应格式
|
|
1075
|
+
* @param data - 响应数据
|
|
1076
|
+
* @param message - 响应消息(可选)
|
|
1077
|
+
* @param code - 响应状态码(可选,默认200)
|
|
1078
|
+
* @returns 格式化的成功API响应对象
|
|
1079
|
+
*/
|
|
1080
|
+
const createSuccessResponse = (data$1, message, code = 200) => {
|
|
1081
|
+
return {
|
|
1082
|
+
success: true,
|
|
1083
|
+
data: data$1,
|
|
1084
|
+
message,
|
|
1085
|
+
code,
|
|
1086
|
+
error: void 0
|
|
1087
|
+
};
|
|
1270
1088
|
};
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
指定评论回复数据: "/fetch_video_comment_replies",
|
|
1287
|
-
弹幕数据: "/fetch_work_danmaku"
|
|
1089
|
+
/**
|
|
1090
|
+
* 创建失败响应格式
|
|
1091
|
+
* @param error - 错误信息
|
|
1092
|
+
* @param message - 详细错误消息(可选)
|
|
1093
|
+
* @param code - 错误状态码(可选,默认500)
|
|
1094
|
+
* @returns 格式化的错误响应对象
|
|
1095
|
+
*/
|
|
1096
|
+
const createErrorResponse = (error, message, code = 500) => {
|
|
1097
|
+
return {
|
|
1098
|
+
success: false,
|
|
1099
|
+
error,
|
|
1100
|
+
message,
|
|
1101
|
+
code,
|
|
1102
|
+
data: void 0
|
|
1103
|
+
};
|
|
1288
1104
|
};
|
|
1289
1105
|
|
|
1290
1106
|
//#endregion
|
|
1291
|
-
//#region src/
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1107
|
+
//#region src/model/networks.ts
|
|
1108
|
+
/** 可恢复的错误代码列表 */
|
|
1109
|
+
const RECOVERABLE_ERROR_CODES = [
|
|
1110
|
+
"ECONNRESET",
|
|
1111
|
+
"ETIMEDOUT",
|
|
1112
|
+
"ECONNREFUSED",
|
|
1113
|
+
"ENOTFOUND",
|
|
1114
|
+
"ENETUNREACH",
|
|
1115
|
+
"EHOSTUNREACH",
|
|
1116
|
+
"EPIPE",
|
|
1117
|
+
"EAI_AGAIN",
|
|
1118
|
+
"ECONNABORTED"
|
|
1119
|
+
];
|
|
1120
|
+
/** 默认最大重试次数 */
|
|
1121
|
+
const DEFAULT_MAX_RETRIES = 3;
|
|
1122
|
+
/** 重试延迟基数(毫秒) */
|
|
1123
|
+
const RETRY_DELAY_BASE = 1e3;
|
|
1124
|
+
/**
|
|
1125
|
+
* 判断错误是否可恢复
|
|
1126
|
+
* @param error - Axios错误对象
|
|
1127
|
+
* @returns 是否可恢复
|
|
1128
|
+
*/
|
|
1129
|
+
const isRecoverableError = (error) => {
|
|
1130
|
+
return RECOVERABLE_ERROR_CODES.includes(error.code);
|
|
1305
1131
|
};
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1132
|
+
/**
|
|
1133
|
+
* 延迟函数
|
|
1134
|
+
* @param ms - 延迟毫秒数
|
|
1135
|
+
*/
|
|
1136
|
+
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1137
|
+
/**
|
|
1138
|
+
* 创建网络错误响应
|
|
1139
|
+
* @param error - 错误对象
|
|
1140
|
+
* @param retries - 已重试次数
|
|
1141
|
+
* @returns 符合Result类型的错误响应
|
|
1142
|
+
*/
|
|
1143
|
+
const createNetworkErrorResult = (error, retries) => {
|
|
1144
|
+
const errorCode = error.code ?? "UNKNOWN";
|
|
1145
|
+
const message = `网络请求失败 [${errorCode}]: ${error.message} (已重试 ${retries} 次)`;
|
|
1146
|
+
return createErrorResponse({
|
|
1147
|
+
code: amagiAPIErrorCode.UNKNOWN,
|
|
1148
|
+
data: null,
|
|
1149
|
+
amagiError: {
|
|
1150
|
+
errorDescription: `${error.message} (已重试 ${retries} 次)`,
|
|
1151
|
+
requestType: error.config?.method?.toUpperCase() ?? "UNKNOWN",
|
|
1152
|
+
requestUrl: error.config?.url ?? "",
|
|
1153
|
+
responseCode: errorCode
|
|
1154
|
+
},
|
|
1155
|
+
amagiMessage: error.message
|
|
1156
|
+
}, message, 500);
|
|
1310
1157
|
};
|
|
1311
|
-
|
|
1312
|
-
//#endregion
|
|
1313
|
-
//#region src/platform/xiaohongshu/sign/index.ts
|
|
1314
1158
|
/**
|
|
1315
|
-
*
|
|
1159
|
+
* 清理User-Agent中的Edge标识,确保请求兼容性
|
|
1160
|
+
* @param userAgent - 原始User-Agent字符串
|
|
1161
|
+
* @returns 清理后的User-Agent字符串
|
|
1316
1162
|
*/
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
* @param url - 请求URL
|
|
1344
|
-
* @param body - 请求体
|
|
1345
|
-
* @param userAgent - User-Agent(暂未使用)
|
|
1346
|
-
* @param method - 请求方法,默认为 'POST'
|
|
1347
|
-
* @param a1Cookie - a1 cookie值
|
|
1348
|
-
* @returns X-S签名
|
|
1349
|
-
*/
|
|
1350
|
-
static generateXS(url$1, body, userAgent, method = "POST", a1Cookie = "") {
|
|
1351
|
-
try {
|
|
1352
|
-
const urlObj = new URL(url$1);
|
|
1353
|
-
const path$1 = urlObj.pathname + urlObj.search;
|
|
1354
|
-
if (method.toUpperCase() === "GET") {
|
|
1355
|
-
const params = typeof body === "object" ? body : {};
|
|
1356
|
-
return this.generateXSGet(path$1, a1Cookie, "xhs-pc-web", params);
|
|
1357
|
-
} else {
|
|
1358
|
-
const requestBody = typeof body === "object" ? body : {};
|
|
1359
|
-
return this.generateXSPost(path$1, a1Cookie, "xhs-pc-web", requestBody);
|
|
1163
|
+
const cleanUserAgent = (userAgent) => {
|
|
1164
|
+
return userAgent.replace(/\s+Edg\/[\d\.]+/g, "");
|
|
1165
|
+
};
|
|
1166
|
+
/**
|
|
1167
|
+
* 执行网络请求并返回数据(带自动重试)
|
|
1168
|
+
* @param config - axios请求配置
|
|
1169
|
+
* @param maxRetries - 最大重试次数,默认3次
|
|
1170
|
+
* @returns 响应数据或错误结果
|
|
1171
|
+
*/
|
|
1172
|
+
const fetchData = async (config, maxRetries = DEFAULT_MAX_RETRIES) => {
|
|
1173
|
+
const cleanedConfig = { ...config };
|
|
1174
|
+
if (cleanedConfig.headers && cleanedConfig.headers["User-Agent"]) cleanedConfig.headers["User-Agent"] = cleanUserAgent(cleanedConfig.headers["User-Agent"]);
|
|
1175
|
+
let lastError = null;
|
|
1176
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) try {
|
|
1177
|
+
return (await (0, axios.default)({
|
|
1178
|
+
...cleanedConfig,
|
|
1179
|
+
validateStatus: () => true
|
|
1180
|
+
})).data;
|
|
1181
|
+
} catch (error) {
|
|
1182
|
+
if (error instanceof axios.AxiosError) {
|
|
1183
|
+
lastError = error;
|
|
1184
|
+
if (isRecoverableError(error) && attempt < maxRetries) {
|
|
1185
|
+
const delayMs = RETRY_DELAY_BASE * Math.pow(2, attempt);
|
|
1186
|
+
logger.warn(`网络请求失败 [${error.code}],${delayMs}ms 后进行第 ${attempt + 1} 次重试...`);
|
|
1187
|
+
await delay(delayMs);
|
|
1188
|
+
continue;
|
|
1360
1189
|
}
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
throw new Error(`签名生成失败: ${error}`);
|
|
1190
|
+
logger.error("网络请求失败:", error.message);
|
|
1191
|
+
return createNetworkErrorResult(error, attempt);
|
|
1364
1192
|
}
|
|
1193
|
+
throw error;
|
|
1365
1194
|
}
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
}
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1195
|
+
return createNetworkErrorResult(lastError, maxRetries);
|
|
1196
|
+
};
|
|
1197
|
+
const normalizeHeaders = (headers) => {
|
|
1198
|
+
if (headers && typeof headers.toJSON === "function") return headers.toJSON();
|
|
1199
|
+
return headers ?? {};
|
|
1200
|
+
};
|
|
1201
|
+
/**
|
|
1202
|
+
* 执行网络请求并返回完整响应(带自动重试)
|
|
1203
|
+
* @param config - axios请求配置
|
|
1204
|
+
* @param maxRetries - 最大重试次数,默认3次
|
|
1205
|
+
* @returns 完整响应或错误结果
|
|
1206
|
+
*/
|
|
1207
|
+
const fetchResponse = async (config, maxRetries = DEFAULT_MAX_RETRIES) => {
|
|
1208
|
+
const cleanedConfig = { ...config };
|
|
1209
|
+
if (cleanedConfig.headers && cleanedConfig.headers["User-Agent"]) cleanedConfig.headers["User-Agent"] = cleanUserAgent(cleanedConfig.headers["User-Agent"]);
|
|
1210
|
+
let lastError = null;
|
|
1211
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) try {
|
|
1212
|
+
return await (0, axios.default)({
|
|
1213
|
+
...cleanedConfig,
|
|
1214
|
+
validateStatus: () => true
|
|
1215
|
+
});
|
|
1216
|
+
} catch (error) {
|
|
1217
|
+
if (error instanceof axios.AxiosError) {
|
|
1218
|
+
lastError = error;
|
|
1219
|
+
if (isRecoverableError(error) && attempt < maxRetries) {
|
|
1220
|
+
const delayMs = RETRY_DELAY_BASE * Math.pow(2, attempt);
|
|
1221
|
+
logger.warn(`网络请求失败 [${error.code}],${delayMs}ms 后进行第 ${attempt + 1} 次重试...`);
|
|
1222
|
+
await delay(delayMs);
|
|
1223
|
+
continue;
|
|
1224
|
+
}
|
|
1225
|
+
logger.error("网络请求失败:", error.message);
|
|
1226
|
+
return createNetworkErrorResult(error, attempt);
|
|
1227
|
+
}
|
|
1228
|
+
throw error;
|
|
1396
1229
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1230
|
+
return createNetworkErrorResult(lastError, maxRetries);
|
|
1231
|
+
};
|
|
1232
|
+
/**
|
|
1233
|
+
* 判断结果是否为网络错误响应
|
|
1234
|
+
* @param result - 请求结果
|
|
1235
|
+
* @returns 是否为ErrorResult
|
|
1236
|
+
*/
|
|
1237
|
+
const isNetworkErrorResult = (result) => {
|
|
1238
|
+
return result !== null && typeof result === "object" && "success" in result && result.success === false;
|
|
1239
|
+
};
|
|
1240
|
+
/**
|
|
1241
|
+
* 获取响应头和数据(带自动重试)
|
|
1242
|
+
* @param config - axios请求配置
|
|
1243
|
+
* @param maxRetries - 最大重试次数,默认3次
|
|
1244
|
+
* @returns 包含headers和data的对象,或错误结果
|
|
1245
|
+
*/
|
|
1246
|
+
const getHeadersAndData = async (config, maxRetries = DEFAULT_MAX_RETRIES) => {
|
|
1247
|
+
const response = await fetchResponse(config, maxRetries);
|
|
1248
|
+
if ("success" in response && response.success === false) return response;
|
|
1249
|
+
return {
|
|
1250
|
+
headers: normalizeHeaders(response.headers),
|
|
1251
|
+
data: response.data
|
|
1252
|
+
};
|
|
1402
1253
|
};
|
|
1403
1254
|
|
|
1404
1255
|
//#endregion
|
|
1405
|
-
//#region src/platform/
|
|
1256
|
+
//#region src/platform/defaultConfigs.ts
|
|
1406
1257
|
/**
|
|
1407
|
-
*
|
|
1258
|
+
* 根据User-Agent生成对应的Sec-Ch-Ua值
|
|
1259
|
+
* @param userAgent - 用户代理字符串
|
|
1260
|
+
* @returns 对应的Sec-Ch-Ua值
|
|
1408
1261
|
*/
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1262
|
+
const generateSecChUa = (userAgent) => {
|
|
1263
|
+
const chromeMatch = userAgent.match(/Chrome\/(\d+)/);
|
|
1264
|
+
const chromeVersion = chromeMatch ? chromeMatch[1] : "125";
|
|
1265
|
+
return `"Not)A;Brand";v="8", "Chromium";v="${chromeVersion}", "Google Chrome";v="${chromeVersion}"`;
|
|
1266
|
+
};
|
|
1267
|
+
/**
|
|
1268
|
+
* 抖音平台默认请求配置
|
|
1269
|
+
* @param cookie - 用户Cookie
|
|
1270
|
+
* @param requestConfig - 外部请求配置(优先级最高)
|
|
1271
|
+
* @returns 合并后的请求配置
|
|
1272
|
+
*/
|
|
1273
|
+
const getDouyinDefaultConfig = (cookie, requestConfig) => {
|
|
1274
|
+
let finalUserAgent = requestConfig?.headers?.["User-Agent"] ?? "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36";
|
|
1275
|
+
finalUserAgent = finalUserAgent.replace(/\s+Edg\/[\d\.]+/g, "");
|
|
1276
|
+
const defHeaders = {
|
|
1277
|
+
Accept: "application/json, text/plain, */*",
|
|
1278
|
+
"Accept-Encoding": "gzip, deflate, br, zstd",
|
|
1279
|
+
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
|
1280
|
+
Cookie: cookie ? cookie.replace(/\s+/g, "") : "",
|
|
1281
|
+
Priority: "u=1, i",
|
|
1282
|
+
Referer: "https://www.douyin.com/",
|
|
1283
|
+
"Sec-Ch-Ua": generateSecChUa(finalUserAgent),
|
|
1284
|
+
"Sec-Ch-Ua-Mobile": "?0",
|
|
1285
|
+
"Sec-Ch-Ua-Platform": "\"Windows\"",
|
|
1286
|
+
"Sec-Fetch-Dest": "empty",
|
|
1287
|
+
"Sec-Fetch-Mode": "cors",
|
|
1288
|
+
"Sec-Fetch-Site": "same-origin",
|
|
1289
|
+
"User-Agent": finalUserAgent
|
|
1290
|
+
};
|
|
1291
|
+
return {
|
|
1292
|
+
method: "GET",
|
|
1293
|
+
timeout: 1e4,
|
|
1294
|
+
...requestConfig,
|
|
1295
|
+
headers: {
|
|
1296
|
+
...defHeaders,
|
|
1297
|
+
...requestConfig?.headers ?? {}
|
|
1298
|
+
}
|
|
1299
|
+
};
|
|
1300
|
+
};
|
|
1424
1301
|
/**
|
|
1425
|
-
*
|
|
1302
|
+
* B站平台默认请求配置
|
|
1303
|
+
* @param cookie - 用户Cookie
|
|
1304
|
+
* @param requestConfig - 外部请求配置(优先级最高)
|
|
1305
|
+
* @returns 合并后的请求配置
|
|
1426
1306
|
*/
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1307
|
+
const getBilibiliDefaultConfig = (cookie, requestConfig) => {
|
|
1308
|
+
const defHeaders = {
|
|
1309
|
+
Accept: "application/json, text/plain, */*",
|
|
1310
|
+
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
|
1311
|
+
"Accept-Encoding": "gzip, deflate, br",
|
|
1312
|
+
Origin: "https://www.bilibili.com",
|
|
1313
|
+
Referer: "https://www.bilibili.com/",
|
|
1314
|
+
Priority: "u=1, i",
|
|
1315
|
+
"Sec-Ch-Ua": "\"Microsoft Edge\";v=\"141\", \"Chromium\";v=\"141\", \"Not_A Brand\";v=\"24\"",
|
|
1316
|
+
"Sec-Ch-Ua-Mobile": "?0",
|
|
1317
|
+
"Sec-Ch-Ua-Platform": "\"Windows\"",
|
|
1318
|
+
"Sec-Fetch-Dest": "empty",
|
|
1319
|
+
"Sec-Fetch-Mode": "cors",
|
|
1320
|
+
"Sec-Fetch-Site": "same-site",
|
|
1321
|
+
Cookie: cookie ? cookie.replace(/\s+/g, "") : ""
|
|
1322
|
+
};
|
|
1323
|
+
return {
|
|
1324
|
+
method: "GET",
|
|
1325
|
+
timeout: 1e4,
|
|
1326
|
+
...requestConfig,
|
|
1327
|
+
headers: {
|
|
1328
|
+
...defHeaders,
|
|
1329
|
+
...requestConfig?.headers ?? {}
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
};
|
|
1442
1333
|
/**
|
|
1443
|
-
*
|
|
1444
|
-
* @param
|
|
1445
|
-
* @
|
|
1334
|
+
* 快手平台默认请求配置
|
|
1335
|
+
* @param cookie - 用户Cookie
|
|
1336
|
+
* @param requestConfig - 外部请求配置(优先级最高)
|
|
1337
|
+
* @returns 合并后的请求配置
|
|
1446
1338
|
*/
|
|
1447
|
-
const
|
|
1448
|
-
|
|
1339
|
+
const getKuaishouDefaultConfig = (cookie, requestConfig) => {
|
|
1340
|
+
const defHeaders = {
|
|
1341
|
+
Referer: "https://www.kuaishou.com/new-reco",
|
|
1342
|
+
Origin: "https://www.kuaishou.com",
|
|
1343
|
+
Accept: "application/json, text/plain, */*",
|
|
1344
|
+
"Accept-Encoding": "gzip, deflate, br, zstd",
|
|
1345
|
+
"Content-Type": "application/json",
|
|
1346
|
+
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
|
1347
|
+
Priority: "u=0, i",
|
|
1348
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0",
|
|
1349
|
+
Cookie: cookie ? cookie.replace(/\s+/g, "") : ""
|
|
1350
|
+
};
|
|
1351
|
+
return {
|
|
1352
|
+
method: "POST",
|
|
1353
|
+
timeout: 1e4,
|
|
1354
|
+
...requestConfig,
|
|
1355
|
+
headers: {
|
|
1356
|
+
...defHeaders,
|
|
1357
|
+
...requestConfig?.headers ?? {}
|
|
1358
|
+
}
|
|
1359
|
+
};
|
|
1449
1360
|
};
|
|
1450
1361
|
/**
|
|
1451
|
-
*
|
|
1362
|
+
* 获取小红书默认配置
|
|
1363
|
+
* @param cookie - 用户Cookie
|
|
1364
|
+
* @returns 小红书请求配置
|
|
1452
1365
|
*/
|
|
1453
|
-
const
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1366
|
+
const getXiaohongshuDefaultConfig = (cookie) => {
|
|
1367
|
+
return { headers: {
|
|
1368
|
+
accept: "application/json, text/plain, */*",
|
|
1369
|
+
"accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
|
|
1370
|
+
"cache-control": "no-cache",
|
|
1371
|
+
"content-type": "application/json;charset=UTF-8",
|
|
1372
|
+
pragma: "no-cache",
|
|
1373
|
+
priority: "u=1, i",
|
|
1374
|
+
referer: "https://www.xiaohongshu.com/",
|
|
1375
|
+
"sec-ch-ua": "\"Microsoft Edge\";v=\"141\", \"Not?A_Brand\";v=\"8\", \"Chromium\";v=\"141\"",
|
|
1376
|
+
"sec-ch-ua-mobile": "?0",
|
|
1377
|
+
"sec-ch-ua-platform": "\"Windows\"",
|
|
1378
|
+
"sec-fetch-dest": "empty",
|
|
1379
|
+
"sec-fetch-mode": "cors",
|
|
1380
|
+
"sec-fetch-site": "same-site",
|
|
1381
|
+
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0",
|
|
1382
|
+
cookie: cookie ?? ""
|
|
1383
|
+
} };
|
|
1384
|
+
};
|
|
1385
|
+
|
|
1386
|
+
//#endregion
|
|
1387
|
+
//#region src/platform/bilibili/API.ts
|
|
1388
|
+
var BiLiBiLiAPI = class {
|
|
1389
|
+
登录基本信息() {
|
|
1390
|
+
return "https://api.bilibili.com/x/web-interface/nav";
|
|
1391
|
+
}
|
|
1392
|
+
视频详细信息(data$1) {
|
|
1393
|
+
return `https://api.bilibili.com/x/web-interface/view?bvid=${data$1.bvid}`;
|
|
1394
|
+
}
|
|
1395
|
+
视频流信息(data$1) {
|
|
1396
|
+
return `https://api.bilibili.com/x/player/playurl?avid=${data$1.avid}&cid=${data$1.cid}`;
|
|
1397
|
+
}
|
|
1398
|
+
/** 评论区类型,type参数详见 [评论区类型代码](https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/comment/readme.md#评论区类型代码) */
|
|
1399
|
+
评论区明细(data$1) {
|
|
1400
|
+
const params = new URLSearchParams({
|
|
1401
|
+
oid: data$1.oid.toString(),
|
|
1402
|
+
type: data$1.type.toString(),
|
|
1403
|
+
mode: (data$1.mode ?? 3).toString(),
|
|
1404
|
+
plat: "1",
|
|
1405
|
+
seek_rpid: "",
|
|
1406
|
+
web_location: "1315875"
|
|
1407
|
+
});
|
|
1408
|
+
if (data$1.pagination_str) params.append("pagination_str", JSON.stringify({ offset: data$1.pagination_str }));
|
|
1409
|
+
else params.append("pagination_str", JSON.stringify({ offset: "" }));
|
|
1410
|
+
return `https://api.bilibili.com/x/v2/reply/wbi/main?${params.toString()}`;
|
|
1411
|
+
}
|
|
1412
|
+
评论区状态(data$1) {
|
|
1413
|
+
return `https://api.bilibili.com/x/v2/reply/subject/description?type=${data$1.type}&oid=${data$1.oid}`;
|
|
1414
|
+
}
|
|
1415
|
+
/** 指定评论的回复 */
|
|
1416
|
+
指定评论的回复(data$1) {
|
|
1417
|
+
return `https://api.bilibili.com/x/v2/reply/reply?type=${data$1.type}&oid=${data$1.oid}&root=${data$1.root}&ps=${data$1.number}`;
|
|
1418
|
+
}
|
|
1419
|
+
表情列表() {
|
|
1420
|
+
return "https://api.bilibili.com/x/emote/user/panel/web?business=reply&web_location=0.0";
|
|
1421
|
+
}
|
|
1422
|
+
番剧明细(data$1) {
|
|
1423
|
+
if (data$1.ep_id) return `https://api.bilibili.com/pgc/view/web/season?ep_id=${data$1.ep_id}`;
|
|
1424
|
+
else if (data$1.season_id) return `https://api.bilibili.com/pgc/view/web/season?season_id=${data$1.season_id}`;
|
|
1425
|
+
else throw new Error("拟造接口地址出错,缺少 ep_id 或 season_id 参数");
|
|
1426
|
+
}
|
|
1427
|
+
番剧视频流信息(data$1) {
|
|
1428
|
+
return `https://api.bilibili.com/pgc/player/web/playurl?cid=${data$1.cid}&ep_id=${data$1.ep_id}`;
|
|
1429
|
+
}
|
|
1430
|
+
用户空间动态(data$1) {
|
|
1431
|
+
return `https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?host_mid=${data$1.host_mid}&dm_img_switch=0&&features=itemOpusStyle,listOnlyfans,opusBigCover,onlyfansVote,forwardListHidden,decorationCard,commentsNewVersion,onlyfansAssetsV2,ugcDelete,onlyfansQaCard`;
|
|
1432
|
+
}
|
|
1433
|
+
动态详情(data$1) {
|
|
1434
|
+
return `https://api.bilibili.com/x/polymer/web-dynamic/v1/detail?id=${data$1.dynamic_id}&features=itemOpusStyle,opusBigCover,onlyfansVote,endFooterHidden,decorationCard,onlyfansAssetsV2,ugcDelete,onlyfansQaCard,editable,opusPrivateVisible,avatarAutoTheme`;
|
|
1435
|
+
}
|
|
1436
|
+
动态卡片信息(data$1) {
|
|
1437
|
+
return `https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/get_dynamic_detail?dynamic_id=${data$1.dynamic_id}`;
|
|
1438
|
+
}
|
|
1439
|
+
用户名片信息(data$1) {
|
|
1440
|
+
return `https://api.bilibili.com/x/web-interface/card?mid=${data$1.host_mid}&photo=true`;
|
|
1441
|
+
}
|
|
1442
|
+
直播间信息(data$1) {
|
|
1443
|
+
return `https://api.live.bilibili.com/room/v1/Room/get_info?room_id=${data$1.room_id}`;
|
|
1444
|
+
}
|
|
1445
|
+
直播间初始化信息(data$1) {
|
|
1446
|
+
return `https://api.live.bilibili.com/room/v1/Room/room_init?id=${data$1.room_id}`;
|
|
1447
|
+
}
|
|
1448
|
+
申请二维码() {
|
|
1449
|
+
return "https://passport.bilibili.com/x/passport-login/web/qrcode/generate";
|
|
1450
|
+
}
|
|
1451
|
+
二维码状态(data$1) {
|
|
1452
|
+
return `https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key=${data$1.qrcode_key}`;
|
|
1453
|
+
}
|
|
1454
|
+
获取UP主总播放量(data$1) {
|
|
1455
|
+
return `https://api.bilibili.com/x/space/upstat?mid=${data$1.host_mid}`;
|
|
1456
|
+
}
|
|
1457
|
+
专栏正文内容(data$1) {
|
|
1458
|
+
return `https://api.bilibili.com/x/article/view?id=${data$1.id}`;
|
|
1459
|
+
}
|
|
1460
|
+
专栏显示卡片信息(data$1) {
|
|
1461
|
+
return `https://api.bilibili.com/x/article/cards?ids=${Array.isArray(data$1.ids) ? data$1.ids.join(",") : data$1.ids}`;
|
|
1462
|
+
}
|
|
1463
|
+
专栏文章基本信息(data$1) {
|
|
1464
|
+
return `https://api.bilibili.com/x/article/viewinfo?id=${data$1.id}`;
|
|
1465
|
+
}
|
|
1466
|
+
文集基本信息(data$1) {
|
|
1467
|
+
return `https://api.bilibili.com/x/article/list/web/articles?id=${data$1.id}`;
|
|
1551
1468
|
}
|
|
1552
1469
|
};
|
|
1470
|
+
/** 该类下的所有方法只会返回拼接好参数后的 Url 地址,需要手动请求该地址以获取数据 */
|
|
1471
|
+
const bilibiliApiUrls = new BiLiBiLiAPI();
|
|
1472
|
+
|
|
1473
|
+
//#endregion
|
|
1474
|
+
//#region src/platform/bilibili/BilibiliApi.ts
|
|
1553
1475
|
/**
|
|
1554
|
-
*
|
|
1555
|
-
* @
|
|
1476
|
+
* 创建B站API方法的通用工厂函数
|
|
1477
|
+
* @template T - B站方法类型键名
|
|
1478
|
+
* @param methodType - 方法类型
|
|
1479
|
+
* @returns 返回配置好的API方法
|
|
1556
1480
|
*/
|
|
1557
|
-
const
|
|
1558
|
-
return
|
|
1481
|
+
const createBilibiliApiMethod = (methodType) => {
|
|
1482
|
+
return async (options, cookie) => {
|
|
1483
|
+
return await getBilibiliData(methodType, options, cookie);
|
|
1484
|
+
};
|
|
1559
1485
|
};
|
|
1560
|
-
|
|
1561
|
-
//#endregion
|
|
1562
|
-
//#region src/validation/xiaohongshu.ts
|
|
1563
|
-
const SearchSortTypeValues = Object.values(SearchSortType).filter((v) => typeof v === "string");
|
|
1564
|
-
const SearchNoteTypeValues = Object.values(SearchNoteType).filter((v) => typeof v === "number");
|
|
1565
1486
|
/**
|
|
1566
|
-
*
|
|
1487
|
+
* 创建绑定cookie的B站API方法工厂函数
|
|
1488
|
+
* @template T - B站方法类型键名
|
|
1489
|
+
* @param methodType - 方法类型
|
|
1490
|
+
* @param cookie - 绑定的cookie
|
|
1491
|
+
* @returns 返回绑定了cookie的API方法
|
|
1567
1492
|
*/
|
|
1568
|
-
const
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
note_index: zod.default.coerce.number({ error: "note_index必须是数字" }).int({ error: "note_index必须是整数" }).optional(),
|
|
1574
|
-
category: zod.default.string({ error: "category必须是字符串" }).optional(),
|
|
1575
|
-
search_key: zod.default.string({ error: "search_key必须是字符串" }).optional()
|
|
1576
|
-
});
|
|
1493
|
+
const createBoundBilibiliApiMethod = (methodType, cookie) => {
|
|
1494
|
+
return async (options) => {
|
|
1495
|
+
return await getBilibiliData(methodType, options, cookie);
|
|
1496
|
+
};
|
|
1497
|
+
};
|
|
1577
1498
|
/**
|
|
1578
|
-
*
|
|
1499
|
+
* B站相关 API 的命名空间。
|
|
1500
|
+
*
|
|
1501
|
+
* 部分接口可能不需要 Cookie 但建议传递有效的用户 Cookie,以获取更多数据。
|
|
1502
|
+
*
|
|
1503
|
+
* 提供了一系列方法,用于与B站相关的 API 进行交互。
|
|
1504
|
+
*
|
|
1505
|
+
* 每个方法都接受参数和 Cookie,返回 Promise,解析为统一格式的API响应。
|
|
1579
1506
|
*/
|
|
1580
|
-
const
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1507
|
+
const bilibili = {
|
|
1508
|
+
getVideoInfo: createBilibiliApiMethod("单个视频作品数据"),
|
|
1509
|
+
getVideoStream: createBilibiliApiMethod("单个视频下载信息数据"),
|
|
1510
|
+
getComments: createBilibiliApiMethod("评论数据"),
|
|
1511
|
+
getCommentReply: createBilibiliApiMethod("指定评论的回复"),
|
|
1512
|
+
getUserProfile: createBilibiliApiMethod("用户主页数据"),
|
|
1513
|
+
getUserDynamic: createBilibiliApiMethod("用户主页动态列表数据"),
|
|
1514
|
+
getEmojiList: createBilibiliApiMethod("Emoji数据"),
|
|
1515
|
+
getBangumiInfo: createBilibiliApiMethod("番剧基本信息数据"),
|
|
1516
|
+
getBangumiStream: createBilibiliApiMethod("番剧下载信息数据"),
|
|
1517
|
+
getDynamicInfo: createBilibiliApiMethod("动态详情数据"),
|
|
1518
|
+
getDynamicCard: createBilibiliApiMethod("动态卡片数据"),
|
|
1519
|
+
getLiveRoomDetail: createBilibiliApiMethod("直播间信息"),
|
|
1520
|
+
getLiveRoomInitInfo: createBilibiliApiMethod("直播间初始化信息"),
|
|
1521
|
+
getLoginBasicInfo: createBilibiliApiMethod("登录基本信息"),
|
|
1522
|
+
getLoginQrcode: createBilibiliApiMethod("申请二维码"),
|
|
1523
|
+
checkQrcodeStatus: createBilibiliApiMethod("二维码状态"),
|
|
1524
|
+
getUserTotalPlayCount: createBilibiliApiMethod("获取UP主总播放量"),
|
|
1525
|
+
convertAvToBv: createBilibiliApiMethod("AV转BV"),
|
|
1526
|
+
convertBvToAv: createBilibiliApiMethod("BV转AV"),
|
|
1527
|
+
getArticleContent: createBilibiliApiMethod("专栏正文内容"),
|
|
1528
|
+
getArticleCard: createBilibiliApiMethod("专栏显示卡片信息"),
|
|
1529
|
+
getArticleInfo: createBilibiliApiMethod("专栏文章基本信息"),
|
|
1530
|
+
getColumnInfo: createBilibiliApiMethod("文集基本信息")
|
|
1531
|
+
};
|
|
1585
1532
|
/**
|
|
1586
|
-
*
|
|
1533
|
+
* 创建绑定了cookie的B站API对象
|
|
1534
|
+
* @param cookie - 要绑定的cookie(可选)
|
|
1535
|
+
* @returns 绑定了cookie的B站API对象,调用时不需要再传递cookie
|
|
1587
1536
|
*/
|
|
1588
|
-
const
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1537
|
+
const createBoundBilibiliApi = (cookie, requestConfig) => {
|
|
1538
|
+
return {
|
|
1539
|
+
getVideoInfo: createBoundBilibiliApiMethod("单个视频作品数据", cookie),
|
|
1540
|
+
getVideoStream: createBoundBilibiliApiMethod("单个视频下载信息数据", cookie),
|
|
1541
|
+
getComments: createBoundBilibiliApiMethod("评论数据", cookie),
|
|
1542
|
+
getCommentReply: createBoundBilibiliApiMethod("指定评论的回复", cookie),
|
|
1543
|
+
getUserProfile: createBoundBilibiliApiMethod("用户主页数据", cookie),
|
|
1544
|
+
getUserDynamic: createBoundBilibiliApiMethod("用户主页动态列表数据", cookie),
|
|
1545
|
+
getEmojiList: createBoundBilibiliApiMethod("Emoji数据", cookie),
|
|
1546
|
+
getBangumiInfo: createBoundBilibiliApiMethod("番剧基本信息数据", cookie),
|
|
1547
|
+
getBangumiStream: createBoundBilibiliApiMethod("番剧下载信息数据", cookie),
|
|
1548
|
+
getDynamicInfo: createBoundBilibiliApiMethod("动态详情数据", cookie),
|
|
1549
|
+
getDynamicCard: createBoundBilibiliApiMethod("动态卡片数据", cookie),
|
|
1550
|
+
getLiveRoomDetail: createBoundBilibiliApiMethod("直播间信息", cookie),
|
|
1551
|
+
getLiveRoomInitInfo: createBoundBilibiliApiMethod("直播间初始化信息", cookie),
|
|
1552
|
+
getLoginBasicInfo: createBoundBilibiliApiMethod("登录基本信息", cookie),
|
|
1553
|
+
getLoginQrcode: createBoundBilibiliApiMethod("申请二维码", cookie),
|
|
1554
|
+
checkQrcodeStatus: createBoundBilibiliApiMethod("二维码状态", cookie),
|
|
1555
|
+
getUserTotalPlayCount: createBoundBilibiliApiMethod("获取UP主总播放量", cookie),
|
|
1556
|
+
convertAvToBv: createBoundBilibiliApiMethod("AV转BV", cookie),
|
|
1557
|
+
convertBvToAv: createBoundBilibiliApiMethod("BV转AV", cookie),
|
|
1558
|
+
getArticleContent: createBoundBilibiliApiMethod("专栏正文内容", cookie),
|
|
1559
|
+
getArticleCard: createBoundBilibiliApiMethod("专栏显示卡片信息", cookie),
|
|
1560
|
+
getArticleInfo: createBoundBilibiliApiMethod("专栏文章基本信息", cookie),
|
|
1561
|
+
getColumnInfo: createBoundBilibiliApiMethod("文集基本信息", cookie)
|
|
1562
|
+
};
|
|
1563
|
+
};
|
|
1564
|
+
|
|
1565
|
+
//#endregion
|
|
1566
|
+
//#region src/platform/bilibili/sign/bv2av.ts
|
|
1567
|
+
const XOR_CODE = 23442827791579n;
|
|
1568
|
+
const MASK_CODE = 2251799813685247n;
|
|
1569
|
+
const MAX_AID = 1n << 51n;
|
|
1570
|
+
const BASE = 58n;
|
|
1571
|
+
const data = "FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf";
|
|
1594
1572
|
/**
|
|
1595
|
-
*
|
|
1573
|
+
* av号转bv号
|
|
1574
|
+
* @param aid av号
|
|
1575
|
+
* @returns
|
|
1596
1576
|
*/
|
|
1597
|
-
const
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1577
|
+
const av2bv = (aid) => {
|
|
1578
|
+
const bytes = [
|
|
1579
|
+
"B",
|
|
1580
|
+
"V",
|
|
1581
|
+
"1",
|
|
1582
|
+
"0",
|
|
1583
|
+
"0",
|
|
1584
|
+
"0",
|
|
1585
|
+
"0",
|
|
1586
|
+
"0",
|
|
1587
|
+
"0",
|
|
1588
|
+
"0",
|
|
1589
|
+
"0",
|
|
1590
|
+
"0"
|
|
1591
|
+
];
|
|
1592
|
+
let bvIndex = bytes.length - 1;
|
|
1593
|
+
let tmp = (MAX_AID | BigInt(aid)) ^ XOR_CODE;
|
|
1594
|
+
while (tmp > 0) {
|
|
1595
|
+
bytes[bvIndex] = data[Number(tmp % BigInt(BASE))];
|
|
1596
|
+
tmp = tmp / BASE;
|
|
1597
|
+
bvIndex -= 1;
|
|
1598
|
+
}
|
|
1599
|
+
[bytes[3], bytes[9]] = [bytes[9], bytes[3]];
|
|
1600
|
+
[bytes[4], bytes[7]] = [bytes[7], bytes[4]];
|
|
1601
|
+
return bytes.join("");
|
|
1602
|
+
};
|
|
1603
|
+
/**
|
|
1604
|
+
* bv号转av号
|
|
1605
|
+
* @param bvid bv号
|
|
1606
|
+
* @returns
|
|
1607
|
+
*/
|
|
1608
|
+
const bv2av = (bvid) => {
|
|
1609
|
+
const bvidArr = Array.from(bvid);
|
|
1610
|
+
[bvidArr[3], bvidArr[9]] = [bvidArr[9], bvidArr[3]];
|
|
1611
|
+
[bvidArr[4], bvidArr[7]] = [bvidArr[7], bvidArr[4]];
|
|
1612
|
+
bvidArr.splice(0, 3);
|
|
1613
|
+
const tmp = bvidArr.reduce((pre, bvidChar) => pre * BASE + BigInt(data.indexOf(bvidChar)), 0n);
|
|
1614
|
+
return Number(tmp & MASK_CODE ^ XOR_CODE);
|
|
1615
|
+
};
|
|
1616
|
+
|
|
1617
|
+
//#endregion
|
|
1618
|
+
//#region src/platform/bilibili/sign/wbi.ts
|
|
1619
|
+
/**
|
|
1620
|
+
* 混合密钥编码表,用于对 imgKey 和 subKey 进行字符顺序打乱编码
|
|
1621
|
+
*/
|
|
1622
|
+
const mixinKeyEncTab = [
|
|
1623
|
+
46,
|
|
1624
|
+
47,
|
|
1625
|
+
18,
|
|
1626
|
+
2,
|
|
1627
|
+
53,
|
|
1628
|
+
8,
|
|
1629
|
+
23,
|
|
1630
|
+
32,
|
|
1631
|
+
15,
|
|
1632
|
+
50,
|
|
1633
|
+
10,
|
|
1634
|
+
31,
|
|
1635
|
+
58,
|
|
1636
|
+
3,
|
|
1637
|
+
45,
|
|
1638
|
+
35,
|
|
1639
|
+
27,
|
|
1640
|
+
43,
|
|
1641
|
+
5,
|
|
1642
|
+
49,
|
|
1643
|
+
33,
|
|
1644
|
+
9,
|
|
1645
|
+
42,
|
|
1646
|
+
19,
|
|
1647
|
+
29,
|
|
1648
|
+
28,
|
|
1649
|
+
14,
|
|
1650
|
+
39,
|
|
1651
|
+
12,
|
|
1652
|
+
38,
|
|
1653
|
+
41,
|
|
1654
|
+
13,
|
|
1655
|
+
37,
|
|
1656
|
+
48,
|
|
1657
|
+
7,
|
|
1658
|
+
16,
|
|
1659
|
+
24,
|
|
1660
|
+
55,
|
|
1661
|
+
40,
|
|
1662
|
+
61,
|
|
1663
|
+
26,
|
|
1664
|
+
17,
|
|
1665
|
+
0,
|
|
1666
|
+
1,
|
|
1667
|
+
60,
|
|
1668
|
+
51,
|
|
1669
|
+
30,
|
|
1670
|
+
4,
|
|
1671
|
+
22,
|
|
1672
|
+
25,
|
|
1673
|
+
54,
|
|
1674
|
+
21,
|
|
1675
|
+
56,
|
|
1676
|
+
59,
|
|
1677
|
+
6,
|
|
1678
|
+
63,
|
|
1679
|
+
57,
|
|
1680
|
+
62,
|
|
1681
|
+
11,
|
|
1682
|
+
36,
|
|
1683
|
+
20,
|
|
1684
|
+
34,
|
|
1685
|
+
44,
|
|
1686
|
+
52
|
|
1687
|
+
];
|
|
1601
1688
|
/**
|
|
1602
|
-
*
|
|
1689
|
+
* 对 imgKey 和 subKey 进行字符顺序打乱编码
|
|
1690
|
+
* @param orig - 原始字符串数组,通常是 img_key + sub_key 的字符数组
|
|
1691
|
+
* @returns 返回经过编码表打乱后的32位字符串
|
|
1603
1692
|
*/
|
|
1604
|
-
const
|
|
1605
|
-
methodType: zod.default.literal("用户笔记数据", { error: "方法类型必须是\"用户笔记数据\"" }),
|
|
1606
|
-
user_id: zod.default.string({ error: "user_id必须是字符串" }),
|
|
1607
|
-
cursor: zod.default.string({ error: "cursor必须是字符串" }).optional(),
|
|
1608
|
-
num: zod.default.coerce.number({ error: "数量必须是数字" }).int({ error: "数量必须是整数" }).min(1, { error: "数量不能小于1" }).max(100, { error: "数量不能大于100" }).optional()
|
|
1609
|
-
});
|
|
1610
|
-
const EmojiListParamsSchema = zod.default.object({ methodType: zod.default.literal("表情列表", { error: "方法类型必须是\"表情列表\"" }) });
|
|
1693
|
+
const getMixinKey = (orig) => mixinKeyEncTab.map((n) => orig[n]).join("").slice(0, 32);
|
|
1611
1694
|
/**
|
|
1612
|
-
*
|
|
1695
|
+
* 为请求参数进行 WBI 签名
|
|
1696
|
+
* @param params - 请求参数对象,键值对形式
|
|
1697
|
+
* @param img_key - 图片密钥
|
|
1698
|
+
* @param sub_key - 子密钥
|
|
1699
|
+
* @returns 返回包含时间戳和签名的查询字符串
|
|
1613
1700
|
*/
|
|
1614
|
-
const
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
})
|
|
1701
|
+
const encWbi = (params, img_key, sub_key) => {
|
|
1702
|
+
const mixin_key = getMixinKey(img_key + sub_key);
|
|
1703
|
+
const curr_time = Math.round(Date.now() / 1e3);
|
|
1704
|
+
const chr_filter = /[!'()*]/g;
|
|
1705
|
+
Object.assign(params, { wts: curr_time });
|
|
1706
|
+
const query = Object.keys(params).sort().map((key) => {
|
|
1707
|
+
const value = params[key].toString().replace(chr_filter, "");
|
|
1708
|
+
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
|
|
1709
|
+
}).join("&");
|
|
1710
|
+
return `&wts=${curr_time}&w_rid=${node_crypto.default.createHash("md5").update(query + mixin_key).digest("hex")}`;
|
|
1711
|
+
};
|
|
1622
1712
|
/**
|
|
1623
|
-
*
|
|
1713
|
+
* 获取最新的 img_key 和 sub_key
|
|
1714
|
+
* @param cookie - 有效的用户 Cookie 字符串
|
|
1715
|
+
* @returns 返回包含 img_key 和 sub_key 的对象
|
|
1716
|
+
* @throws 当网络请求失败或响应格式不正确时抛出错误
|
|
1624
1717
|
*/
|
|
1625
|
-
const
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
表情列表: EmojiListParamsSchema,
|
|
1632
|
-
搜索笔记: SearchNoteParamsSchema
|
|
1718
|
+
const getWbiKeys = async (cookie) => {
|
|
1719
|
+
const { data: { wbi_img: { img_url, sub_url } } } = (await (0, axios.default)("https://api.bilibili.com/x/web-interface/nav", { headers: { Cookie: cookie } })).data;
|
|
1720
|
+
return {
|
|
1721
|
+
img_key: img_url.slice(img_url.lastIndexOf("/") + 1, img_url.lastIndexOf(".")),
|
|
1722
|
+
sub_key: sub_url.slice(sub_url.lastIndexOf("/") + 1, sub_url.lastIndexOf("."))
|
|
1723
|
+
};
|
|
1633
1724
|
};
|
|
1634
1725
|
/**
|
|
1635
|
-
*
|
|
1726
|
+
* 对请求链接进行 WBI 签名
|
|
1727
|
+
* @param BASEURL - 完整的请求地址,可以是字符串或 URL 对象
|
|
1728
|
+
* @param cookie - 有效的用户 Cookie 字符串
|
|
1729
|
+
* @returns 返回包含 WBI 签名的查询字符串
|
|
1730
|
+
* @throws 当获取 WBI 密钥失败或 URL 解析失败时抛出错误
|
|
1636
1731
|
*/
|
|
1637
|
-
const
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
表情列表: "/fetch_emoji_list",
|
|
1644
|
-
搜索笔记: "/fetch_search_notes"
|
|
1732
|
+
const wbi_sign = async (BASEURL, cookie) => {
|
|
1733
|
+
const web_keys = await getWbiKeys(cookie);
|
|
1734
|
+
const url$1 = new URL(BASEURL);
|
|
1735
|
+
const params = {};
|
|
1736
|
+
for (const [key, value] of url$1.searchParams.entries()) params[key] = value;
|
|
1737
|
+
return encWbi(params, web_keys.img_key, web_keys.sub_key);
|
|
1645
1738
|
};
|
|
1646
1739
|
|
|
1647
1740
|
//#endregion
|
|
1648
|
-
//#region src/
|
|
1649
|
-
/**
|
|
1650
|
-
* 验证抖音参数
|
|
1651
|
-
* @param methodType - 抖音方法类型
|
|
1652
|
-
* @param params - 待验证的参数
|
|
1653
|
-
* @returns 验证后的参数,符合原始API期望的类型
|
|
1654
|
-
*/
|
|
1655
|
-
const validateDouyinParams = (methodType, params) => {
|
|
1656
|
-
return DouyinValidationSchemas[methodType].parse(typeof params === "object" && params !== null ? {
|
|
1657
|
-
methodType,
|
|
1658
|
-
...params
|
|
1659
|
-
} : {
|
|
1660
|
-
methodType,
|
|
1661
|
-
params
|
|
1662
|
-
});
|
|
1663
|
-
};
|
|
1664
|
-
/**
|
|
1665
|
-
* 验证哔哩哔哩参数
|
|
1666
|
-
* @param methodType - 哔哩哔哩方法类型
|
|
1667
|
-
* @param params - 待验证的参数
|
|
1668
|
-
* @returns 验证后的参数,符合原始API期望的类型
|
|
1669
|
-
*/
|
|
1670
|
-
const validateBilibiliParams = (methodType, params) => {
|
|
1671
|
-
return BilibiliValidationSchemas[methodType].parse(typeof params === "object" && params !== null ? {
|
|
1672
|
-
methodType,
|
|
1673
|
-
...params
|
|
1674
|
-
} : {
|
|
1675
|
-
methodType,
|
|
1676
|
-
params
|
|
1677
|
-
});
|
|
1678
|
-
};
|
|
1741
|
+
//#region src/utils/errors.ts
|
|
1679
1742
|
/**
|
|
1680
|
-
*
|
|
1681
|
-
* @param methodType - 快手方法类型
|
|
1682
|
-
* @param params - 待验证的参数
|
|
1683
|
-
* @returns 验证后的参数,符合原始API期望的类型
|
|
1743
|
+
* API错误类
|
|
1684
1744
|
*/
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1745
|
+
var ApiError = class extends Error {
|
|
1746
|
+
code;
|
|
1747
|
+
platform;
|
|
1748
|
+
/**
|
|
1749
|
+
* 构造API错误
|
|
1750
|
+
* @param message - 错误消息
|
|
1751
|
+
* @param code - 错误代码
|
|
1752
|
+
* @param platform - 平台名称
|
|
1753
|
+
*/
|
|
1754
|
+
constructor(message, code = 500, platform = "unknown") {
|
|
1755
|
+
super(message);
|
|
1756
|
+
this.name = "ApiError";
|
|
1757
|
+
this.code = code;
|
|
1758
|
+
this.platform = platform;
|
|
1759
|
+
}
|
|
1693
1760
|
};
|
|
1694
1761
|
/**
|
|
1695
|
-
*
|
|
1696
|
-
* @param methodType - 小红书方法类型
|
|
1697
|
-
* @param params - 待验证的参数
|
|
1698
|
-
* @returns 验证后的参数
|
|
1762
|
+
* 参数验证错误类
|
|
1699
1763
|
*/
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1764
|
+
var ValidationError = class ValidationError extends Error {
|
|
1765
|
+
errors;
|
|
1766
|
+
requestPath;
|
|
1767
|
+
/**
|
|
1768
|
+
* 构造参数验证错误
|
|
1769
|
+
* @param message - 错误消息
|
|
1770
|
+
* @param errors - 详细错误信息
|
|
1771
|
+
* @param requestPath - HTTP请求路径
|
|
1772
|
+
*/
|
|
1773
|
+
constructor(message, errors, requestPath) {
|
|
1774
|
+
super(message);
|
|
1775
|
+
this.name = "ValidationError";
|
|
1776
|
+
this.errors = errors;
|
|
1777
|
+
this.requestPath = requestPath;
|
|
1778
|
+
}
|
|
1779
|
+
/**
|
|
1780
|
+
* 从Zod错误创建验证错误
|
|
1781
|
+
* @param zodError - Zod验证错误
|
|
1782
|
+
* @param requestPath - HTTP请求路径
|
|
1783
|
+
* @returns 验证错误实例
|
|
1784
|
+
*/
|
|
1785
|
+
static fromZodError(zodError, requestPath) {
|
|
1786
|
+
return new ValidationError("参数验证失败", zodError.issues.map((err) => ({
|
|
1787
|
+
field: err.path.join("."),
|
|
1788
|
+
message: err.message
|
|
1789
|
+
})), requestPath);
|
|
1790
|
+
}
|
|
1708
1791
|
};
|
|
1709
1792
|
/**
|
|
1710
|
-
*
|
|
1711
|
-
* @param
|
|
1712
|
-
* @param
|
|
1713
|
-
* @
|
|
1714
|
-
* @returns 格式化的成功API响应对象
|
|
1793
|
+
* 处理错误并返回统一格式
|
|
1794
|
+
* @param error - 错误对象
|
|
1795
|
+
* @param requestPath - HTTP请求路径(可选)
|
|
1796
|
+
* @returns 统一的错误响应格式
|
|
1715
1797
|
*/
|
|
1716
|
-
const
|
|
1717
|
-
return {
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1798
|
+
const handleError = (error, requestPath) => {
|
|
1799
|
+
if (error instanceof ValidationError) return {
|
|
1800
|
+
code: 400,
|
|
1801
|
+
message: error.message,
|
|
1802
|
+
data: null,
|
|
1803
|
+
errors: error.errors,
|
|
1804
|
+
requestPath: error.requestPath ?? requestPath
|
|
1723
1805
|
};
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
const createErrorResponse = (error, message, code = 500) => {
|
|
1806
|
+
if (error instanceof ApiError) return {
|
|
1807
|
+
code: error.code,
|
|
1808
|
+
message: error.message,
|
|
1809
|
+
data: null,
|
|
1810
|
+
platform: error.platform,
|
|
1811
|
+
requestPath
|
|
1812
|
+
};
|
|
1813
|
+
if (error instanceof zod.default.ZodError) return handleError(ValidationError.fromZodError(error, requestPath), requestPath);
|
|
1733
1814
|
return {
|
|
1734
|
-
|
|
1735
|
-
error,
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
data: void 0
|
|
1815
|
+
code: 500,
|
|
1816
|
+
message: error instanceof Error ? error.message : "未知错误",
|
|
1817
|
+
data: null,
|
|
1818
|
+
requestPath
|
|
1739
1819
|
};
|
|
1740
1820
|
};
|
|
1741
1821
|
|
|
@@ -2088,6 +2168,18 @@ const fetchBilibili = async (data$1, cookie, requestConfig) => {
|
|
|
2088
2168
|
...baseRequestConfig,
|
|
2089
2169
|
url: bilibiliApiUrls.二维码状态({ qrcode_key: data$1.qrcode_key })
|
|
2090
2170
|
});
|
|
2171
|
+
if (isNetworkErrorResult(result)) {
|
|
2172
|
+
const networkError = new Error(result.error.amagiError.errorDescription);
|
|
2173
|
+
Object.assign(networkError, {
|
|
2174
|
+
code: result.error.code,
|
|
2175
|
+
data: null,
|
|
2176
|
+
amagiError: {
|
|
2177
|
+
...result.error.amagiError,
|
|
2178
|
+
requestType: data$1.methodType
|
|
2179
|
+
}
|
|
2180
|
+
});
|
|
2181
|
+
throw networkError;
|
|
2182
|
+
}
|
|
2091
2183
|
if (result.data.code !== 0) {
|
|
2092
2184
|
const Err = {
|
|
2093
2185
|
errorDescription: `获取响应数据失败!原因:${bilibiliErrorCodeMap[String(result.data.code)] || result.data.message || "未知错误"}!`,
|
|
@@ -2169,6 +2261,18 @@ const GlobalGetData$3 = async (type, options, retryCount = 0) => {
|
|
|
2169
2261
|
let warningMessage = "";
|
|
2170
2262
|
try {
|
|
2171
2263
|
const result = await fetchData(options);
|
|
2264
|
+
if (isNetworkErrorResult(result)) {
|
|
2265
|
+
const networkError = new Error(result.error.amagiError.errorDescription);
|
|
2266
|
+
Object.assign(networkError, {
|
|
2267
|
+
code: result.error.code,
|
|
2268
|
+
data: null,
|
|
2269
|
+
amagiError: {
|
|
2270
|
+
...result.error.amagiError,
|
|
2271
|
+
requestType: type
|
|
2272
|
+
}
|
|
2273
|
+
});
|
|
2274
|
+
throw networkError;
|
|
2275
|
+
}
|
|
2172
2276
|
if (!result || result === "") {
|
|
2173
2277
|
const Err = {
|
|
2174
2278
|
errorDescription: "获取响应数据失败!接口返回内容为空,你的B站ck可能已经失效!",
|
|
@@ -3902,6 +4006,18 @@ const GlobalGetData$2 = async (type, config) => {
|
|
|
3902
4006
|
let warningMessage = "";
|
|
3903
4007
|
try {
|
|
3904
4008
|
const result = await fetchData(config);
|
|
4009
|
+
if (isNetworkErrorResult(result)) {
|
|
4010
|
+
const networkError = new Error(result.error.amagiError.errorDescription);
|
|
4011
|
+
Object.assign(networkError, {
|
|
4012
|
+
code: result.error.code,
|
|
4013
|
+
data: null,
|
|
4014
|
+
amagiError: {
|
|
4015
|
+
...result.error.amagiError,
|
|
4016
|
+
requestType: type
|
|
4017
|
+
}
|
|
4018
|
+
});
|
|
4019
|
+
throw networkError;
|
|
4020
|
+
}
|
|
3905
4021
|
if (!result || result === "") {
|
|
3906
4022
|
const Err = {
|
|
3907
4023
|
errorDescription: "获取响应数据失败!接口返回内容为空,你的抖音ck可能已经失效!",
|
|
@@ -4105,6 +4221,18 @@ const GlobalGetData$1 = async (type, options) => {
|
|
|
4105
4221
|
let warningMessage = "";
|
|
4106
4222
|
try {
|
|
4107
4223
|
const result = await fetchData(options);
|
|
4224
|
+
if (isNetworkErrorResult(result)) {
|
|
4225
|
+
const networkError = new Error(result.error.amagiError.errorDescription);
|
|
4226
|
+
Object.assign(networkError, {
|
|
4227
|
+
code: result.error.code,
|
|
4228
|
+
data: null,
|
|
4229
|
+
amagiError: {
|
|
4230
|
+
...result.error.amagiError,
|
|
4231
|
+
requestType: type
|
|
4232
|
+
}
|
|
4233
|
+
});
|
|
4234
|
+
throw networkError;
|
|
4235
|
+
}
|
|
4108
4236
|
if (result === "" || !result || result.result === 2) {
|
|
4109
4237
|
const Err = {
|
|
4110
4238
|
errorDescription: "获取响应数据失败!接口返回内容为空!",
|
|
@@ -4289,6 +4417,18 @@ const XiaohongshuData = async (data$1, cookie, requestConfig) => {
|
|
|
4289
4417
|
const GlobalGetData = async (methodType, config) => {
|
|
4290
4418
|
try {
|
|
4291
4419
|
const response = await fetchData(config);
|
|
4420
|
+
if (isNetworkErrorResult(response)) {
|
|
4421
|
+
const networkError = new Error(response.error.amagiError.errorDescription);
|
|
4422
|
+
Object.assign(networkError, {
|
|
4423
|
+
code: response.error.code,
|
|
4424
|
+
data: null,
|
|
4425
|
+
amagiError: {
|
|
4426
|
+
...response.error.amagiError,
|
|
4427
|
+
requestType: methodType
|
|
4428
|
+
}
|
|
4429
|
+
});
|
|
4430
|
+
throw networkError;
|
|
4431
|
+
}
|
|
4292
4432
|
if (typeof response === "string" && response.includes("<html>")) return response;
|
|
4293
4433
|
if (response.code !== 0) throw new Error(`API请求失败: ${response.data?.msg ?? response.msg ?? "未知错误"}, code: ${response.code}`);
|
|
4294
4434
|
return response;
|
|
@@ -4970,7 +5110,7 @@ let DynamicType = /* @__PURE__ */ function(DynamicType$1) {
|
|
|
4970
5110
|
|
|
4971
5111
|
//#endregion
|
|
4972
5112
|
//#region src/index.ts
|
|
4973
|
-
const VERSION = "5.
|
|
5113
|
+
const VERSION = "5.11.0";
|
|
4974
5114
|
/**
|
|
4975
5115
|
* @deprecated 请使用 createAmagiClient 替代
|
|
4976
5116
|
*/
|
|
@@ -5087,6 +5227,7 @@ exports.getHeadersAndData = getHeadersAndData;
|
|
|
5087
5227
|
exports.getKuaishouData = getKuaishouData;
|
|
5088
5228
|
exports.handleError = handleError;
|
|
5089
5229
|
exports.httpLogger = httpLogger;
|
|
5230
|
+
exports.isNetworkErrorResult = isNetworkErrorResult;
|
|
5090
5231
|
exports.kuaishou = kuaishou;
|
|
5091
5232
|
exports.kuaishouApiUrls = kuaishouApiUrls;
|
|
5092
5233
|
exports.kuaishouUtils = kuaishouUtils;
|