@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.
@@ -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/platform/bilibili/API.ts
527
- var BiLiBiLiAPI = class {
528
- 登录基本信息() {
529
- return "https://api.bilibili.com/x/web-interface/nav";
530
- }
531
- 视频详细信息(data$1) {
532
- return `https://api.bilibili.com/x/web-interface/view?bvid=${data$1.bvid}`;
533
- }
534
- 视频流信息(data$1) {
535
- return `https://api.bilibili.com/x/player/playurl?avid=${data$1.avid}&cid=${data$1.cid}`;
536
- }
537
- /** 评论区类型,type参数详见 [评论区类型代码](https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/comment/readme.md#评论区类型代码) */
538
- 评论区明细(data$1) {
539
- const params = new URLSearchParams({
540
- oid: data$1.oid.toString(),
541
- type: data$1.type.toString(),
542
- mode: (data$1.mode ?? 3).toString(),
543
- plat: "1",
544
- seek_rpid: "",
545
- web_location: "1315875"
546
- });
547
- if (data$1.pagination_str) params.append("pagination_str", JSON.stringify({ offset: data$1.pagination_str }));
548
- else params.append("pagination_str", JSON.stringify({ offset: "" }));
549
- return `https://api.bilibili.com/x/v2/reply/wbi/main?${params.toString()}`;
550
- }
551
- 评论区状态(data$1) {
552
- return `https://api.bilibili.com/x/v2/reply/subject/description?type=${data$1.type}&oid=${data$1.oid}`;
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/platform/bilibili/BilibiliApi.ts
614
- /**
615
- * 创建B站API方法的通用工厂函数
616
- * @template T - B站方法类型键名
617
- * @param methodType - 方法类型
618
- * @returns 返回配置好的API方法
619
- */
620
- const createBilibiliApiMethod = (methodType) => {
621
- return async (options, cookie) => {
622
- return await getBilibiliData(methodType, options, cookie);
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
- * 创建绑定cookie的B站API方法工厂函数
627
- * @template T - B站方法类型键名
628
- * @param methodType - 方法类型
629
- * @param cookie - 绑定的cookie
630
- * @returns 返回绑定了cookie的API方法
631
- */
632
- const createBoundBilibiliApiMethod = (methodType, cookie) => {
633
- return async (options) => {
634
- return await getBilibiliData(methodType, options, cookie);
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
- * B站相关 API 的命名空间。
639
- *
640
- * 部分接口可能不需要 Cookie 但建议传递有效的用户 Cookie,以获取更多数据。
641
- *
642
- * 提供了一系列方法,用于与B站相关的 API 进行交互。
643
- *
644
- * 每个方法都接受参数和 Cookie,返回 Promise,解析为统一格式的API响应。
645
- */
646
- const bilibili = {
647
- getVideoInfo: createBilibiliApiMethod("单个视频作品数据"),
648
- getVideoStream: createBilibiliApiMethod("单个视频下载信息数据"),
649
- getComments: createBilibiliApiMethod("评论数据"),
650
- getCommentReply: createBilibiliApiMethod("指定评论的回复"),
651
- getUserProfile: createBilibiliApiMethod("用户主页数据"),
652
- getUserDynamic: createBilibiliApiMethod("用户主页动态列表数据"),
653
- getEmojiList: createBilibiliApiMethod("Emoji数据"),
654
- getBangumiInfo: createBilibiliApiMethod("番剧基本信息数据"),
655
- getBangumiStream: createBilibiliApiMethod("番剧下载信息数据"),
656
- getDynamicInfo: createBilibiliApiMethod("动态详情数据"),
657
- getDynamicCard: createBilibiliApiMethod("动态卡片数据"),
658
- getLiveRoomDetail: createBilibiliApiMethod("直播间信息"),
659
- getLiveRoomInitInfo: createBilibiliApiMethod("直播间初始化信息"),
660
- getLoginBasicInfo: createBilibiliApiMethod("登录基本信息"),
661
- getLoginQrcode: createBilibiliApiMethod("申请二维码"),
662
- checkQrcodeStatus: createBilibiliApiMethod("二维码状态"),
663
- getUserTotalPlayCount: createBilibiliApiMethod("获取UP主总播放量"),
664
- convertAvToBv: createBilibiliApiMethod("AV转BV"),
665
- convertBvToAv: createBilibiliApiMethod("BV转AV"),
666
- getArticleContent: createBilibiliApiMethod("专栏正文内容"),
667
- getArticleCard: createBilibiliApiMethod("专栏显示卡片信息"),
668
- getArticleInfo: createBilibiliApiMethod("专栏文章基本信息"),
669
- getColumnInfo: createBilibiliApiMethod("文集基本信息")
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
- * 创建绑定了cookie的B站API对象
673
- * @param cookie - 要绑定的cookie(可选)
674
- * @returns 绑定了cookie的B站API对象,调用时不需要再传递cookie
675
- */
676
- const createBoundBilibiliApi = (cookie, requestConfig) => {
677
- return {
678
- getVideoInfo: createBoundBilibiliApiMethod("单个视频作品数据", cookie),
679
- getVideoStream: createBoundBilibiliApiMethod("单个视频下载信息数据", cookie),
680
- getComments: createBoundBilibiliApiMethod("评论数据", cookie),
681
- getCommentReply: createBoundBilibiliApiMethod("指定评论的回复", cookie),
682
- getUserProfile: createBoundBilibiliApiMethod("用户主页数据", cookie),
683
- getUserDynamic: createBoundBilibiliApiMethod("用户主页动态列表数据", cookie),
684
- getEmojiList: createBoundBilibiliApiMethod("Emoji数据", cookie),
685
- getBangumiInfo: createBoundBilibiliApiMethod("番剧基本信息数据", cookie),
686
- getBangumiStream: createBoundBilibiliApiMethod("番剧下载信息数据", cookie),
687
- getDynamicInfo: createBoundBilibiliApiMethod("动态详情数据", cookie),
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/platform/bilibili/sign/bv2av.ts
706
- const XOR_CODE = 23442827791579n;
707
- const MASK_CODE = 2251799813685247n;
708
- const MAX_AID = 1n << 51n;
709
- const BASE = 58n;
710
- const data = "FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf";
711
- /**
712
- * av号转bv号
713
- * @param aid av号
714
- * @returns
715
- */
716
- const av2bv = (aid) => {
717
- const bytes = [
718
- "B",
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
- * bv号转av号
744
- * @param bvid bv号
745
- * @returns
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/bilibili/sign/wbi.ts
677
+ //#region src/platform/xiaohongshu/sign/index.ts
758
678
  /**
759
- * 混合密钥编码表,用于对 imgKey 和 subKey 进行字符顺序打乱编码
679
+ * 小红书签名算法类
760
680
  */
761
- const mixinKeyEncTab = [
762
- 46,
763
- 47,
764
- 18,
765
- 2,
766
- 53,
767
- 8,
768
- 23,
769
- 32,
770
- 15,
771
- 50,
772
- 10,
773
- 31,
774
- 58,
775
- 3,
776
- 45,
777
- 35,
778
- 27,
779
- 43,
780
- 5,
781
- 49,
782
- 33,
783
- 9,
784
- 42,
785
- 19,
786
- 29,
787
- 28,
788
- 14,
789
- 39,
790
- 12,
791
- 38,
792
- 41,
793
- 13,
794
- 37,
795
- 48,
796
- 7,
797
- 16,
798
- 24,
799
- 55,
800
- 40,
801
- 61,
802
- 26,
803
- 17,
804
- 0,
805
- 1,
806
- 60,
807
- 51,
808
- 30,
809
- 4,
810
- 22,
811
- 25,
812
- 54,
813
- 21,
814
- 56,
815
- 59,
816
- 6,
817
- 63,
818
- 57,
819
- 62,
820
- 11,
821
- 36,
822
- 20,
823
- 34,
824
- 44,
825
- 52
826
- ];
827
- /**
828
- * 对 imgKey 和 subKey 进行字符顺序打乱编码
829
- * @param orig - 原始字符串数组,通常是 img_key + sub_key 的字符数组
830
- * @returns 返回经过编码表打乱后的32位字符串
831
- */
832
- const getMixinKey = (orig) => mixinKeyEncTab.map((n) => orig[n]).join("").slice(0, 32);
833
- /**
834
- * 为请求参数进行 WBI 签名
835
- * @param params - 请求参数对象,键值对形式
836
- * @param img_key - 图片密钥
837
- * @param sub_key - 子密钥
838
- * @returns 返回包含时间戳和签名的查询字符串
839
- */
840
- const encWbi = (params, img_key, sub_key) => {
841
- const mixin_key = getMixinKey(img_key + sub_key);
842
- const curr_time = Math.round(Date.now() / 1e3);
843
- const chr_filter = /[!'()*]/g;
844
- Object.assign(params, { wts: curr_time });
845
- const query = Object.keys(params).sort().map((key) => {
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/utils/errors.ts
769
+ //#region src/platform/xiaohongshu/API.ts
881
770
  /**
882
- * API错误类
771
+ * 搜索排序类型枚举
883
772
  */
884
- var ApiError = class extends Error {
885
- code;
886
- platform;
773
+ let SearchSortType = /* @__PURE__ */ function(SearchSortType$1) {
887
774
  /**
888
- * 构造API错误
889
- * @param message - 错误消息
890
- * @param code - 错误代码
891
- * @param platform - 平台名称
775
+ * 默认排序
892
776
  */
893
- constructor(message, code = 500, platform = "unknown") {
894
- super(message);
895
- this.name = "ApiError";
896
- this.code = code;
897
- this.platform = platform;
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
- var ValidationError = class ValidationError extends Error {
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
- constructor(message, errors, requestPath) {
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
- * 从Zod错误创建验证错误
920
- * @param zodError - Zod验证错误
921
- * @param requestPath - HTTP请求路径
922
- * @returns 验证错误实例
797
+ * 仅视频
923
798
  */
924
- static fromZodError(zodError, requestPath) {
925
- return new ValidationError("参数验证失败", zodError.issues.map((err) => ({
926
- field: err.path.join("."),
927
- message: err.message
928
- })), requestPath);
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 handleError = (error, requestPath) => {
938
- if (error instanceof ValidationError) return {
939
- code: 400,
940
- message: error.message,
941
- data: null,
942
- errors: error.errors,
943
- requestPath: error.requestPath ?? requestPath
944
- };
945
- if (error instanceof ApiError) return {
946
- code: error.code,
947
- message: error.message,
948
- data: null,
949
- platform: error.platform,
950
- requestPath
951
- };
952
- if (error instanceof zod.default.ZodError) return handleError(ValidationError.fromZodError(error, requestPath), requestPath);
953
- return {
954
- code: 500,
955
- message: error instanceof Error ? error.message : "未知错误",
956
- data: null,
957
- requestPath
958
- };
959
- };
960
-
961
- //#endregion
962
- //#region src/validation/utils.ts
963
- function smartNumber(errorMessage, minValue = 1, isInteger = false) {
964
- if (isInteger) return zod.default.coerce.number({ error: errorMessage }).int({ error: `${errorMessage.replace("不能为空", "")}必须是整数,不能包含小数` }).min(minValue, { error: `${errorMessage.replace("不能为空", "")}必须大于等于${minValue}` });
965
- else return zod.default.coerce.number({ error: errorMessage }).min(minValue, { error: `${errorMessage.replace("不能为空", "")}必须大于等于${minValue}` });
966
- }
967
- /**
968
- * 智能正整数转换器 - 专门用于正整数类型的转换
969
- * @param errorMessage - 自定义错误信息
970
- * @returns Zod正整数验证器
971
- */
972
- const smartPositiveInteger = (errorMessage) => {
973
- return smartNumber(errorMessage, 1, true);
974
- };
975
- /**
976
- * 从页面HTML中提取用户信息
977
- * @param html - 包含用户页面HTML的字符串
978
- * @returns 提取到的用户信息对象或null
979
- */
980
- const extractCreatorInfoFromHtml = (html) => {
981
- const match = html.match(/<script>window\.__INITIAL_STATE__=(.+)<\/script>/m);
982
- if (!match) return null;
983
- try {
984
- const jsonStr = match[1].replace(/:undefined/g, ":null");
985
- return JSON.parse(jsonStr)?.user?.userPageData ?? null;
986
- } catch (error) {
987
- console.error("解析用户信息失败:", error);
988
- return null;
989
- }
990
- };
991
-
992
- //#endregion
993
- //#region src/validation/bilibili.ts
994
- const BilibiliVideoParamsSchema = zod.default.object({
995
- methodType: zod.default.literal("单个视频作品数据", { error: "方法类型必须是\"单个视频作品数据\"" }),
996
- bvid: zod.default.string({ error: "BVID必须是字符串" }).min(1, { error: "BVID不能为空" })
997
- });
998
- const BilibiliVideoDownloadParamsSchema = zod.default.object({
999
- methodType: zod.default.literal("单个视频下载信息数据", { error: "方法类型必须是\"单个视频下载信息数据\"" }),
1000
- avid: smartNumber("AVID不能为空", 1, true),
1001
- cid: smartNumber("CID不能为空", 1, true)
1002
- });
1003
- const BilibiliCommentParamsSchema = zod.default.object({
1004
- methodType: zod.default.literal("评论数据", { error: "方法类型必须是\"评论数据\"" }),
1005
- oid: zod.default.string({ error: "OID必须是字符串" }).min(1, { error: "OID不能为空" }),
1006
- type: smartNumber("评论类型不能为空", 1, true).refine((val) => [
1007
- 1,
1008
- 2,
1009
- 4,
1010
- 5,
1011
- 6,
1012
- 7,
1013
- 8,
1014
- 9,
1015
- 10,
1016
- 11,
1017
- 12,
1018
- 13,
1019
- 14,
1020
- 15,
1021
- 16,
1022
- 17,
1023
- 18,
1024
- 19,
1025
- 20,
1026
- 21,
1027
- 22,
1028
- 33
1029
- ].includes(val), { error: "无效的评论区类型" }),
1030
- number: zod.default.coerce.number({ error: "评论数量必须是数字" }).int({ error: "评论数量必须是整数" }).positive({ error: "评论数量必须是正数" }).default(20).optional(),
1031
- pn: zod.default.coerce.number({ error: "页码必须是数字" }).int({ error: "页码必须是整数" }).positive({ error: "页码必须是正数" }).default(1).optional()
1032
- });
1033
- const BilibiliCommentReplyParamsSchema = zod.default.object({
1034
- methodType: zod.default.literal("指定评论的回复", { error: "方法类型必须是\"指定评论的回复\"" }),
1035
- oid: zod.default.string({ error: "OID必须是字符串" }).min(1, { error: "OID不能为空" }),
1036
- type: smartNumber("评论类型不能为空", 1, true).refine((val) => [
1037
- 1,
1038
- 2,
1039
- 4,
1040
- 5,
1041
- 6,
1042
- 7,
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/douyin.ts
1177
- const DouyinWorkParamsSchema = zod.default.object({
1178
- methodType: zod.default.enum([
1179
- "视频作品数据",
1180
- "图集作品数据",
1181
- "合辑作品数据",
1182
- "聚合解析"
1183
- ], { error: "方法类型必须是指定的枚举值之一" }),
1184
- aweme_id: zod.default.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" })
1185
- });
1186
- const DouyinCommentParamsSchema = zod.default.object({
1187
- methodType: zod.default.literal("评论数据", { error: "方法类型必须是\"评论数据\"" }),
1188
- aweme_id: zod.default.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" }),
1189
- number: smartPositiveInteger("评论数量必须是正整数").optional().default(50),
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
- const DouyinUserParamsSchema = zod.default.object({
1215
- methodType: zod.default.enum(["用户主页数据", "用户主页视频列表数据"], { error: "方法类型必须是指定的枚举值之一" }),
1216
- sec_uid: zod.default.string({ error: "用户ID必须是字符串" }).min(1, { error: "用户ID不能为空" })
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
- const DouyinMusicParamsSchema = zod.default.object({
1219
- methodType: zod.default.literal("音乐数据", { error: "方法类型必须是\"音乐数据\"" }),
1220
- music_id: zod.default.string({ error: "音乐ID必须是字符串" }).min(1, { error: "音乐ID不能为空" })
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
- const DouyinLiveRoomParamsSchema = zod.default.object({
1223
- methodType: zod.default.literal("直播间信息数据", { error: "方法类型必须是\"直播间信息数据\"" }),
1224
- web_rid: zod.default.string({ error: "直播间ID必须是字符串" }).min(1, { error: "直播间ID不能为空" }),
1225
- room_id: zod.default.string({ error: "直播间ID必须是字符串" }).min(1, { error: "直播间ID不能为空" })
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
- const DouyinQrcodeParamsSchema = zod.default.object({
1228
- methodType: zod.default.literal("申请二维码数据", { error: "方法类型必须是\"申请二维码数据\"" }),
1229
- verify_fp: zod.default.string({ error: "fp指纹必须是字符串" }).min(1, { error: "fp指纹不能为空" })
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 DouyinEmojiListParamsSchema = zod.default.object({ methodType: zod.default.literal("Emoji数据", { error: "方法类型必须是\"Emoji数据\"" }) });
1232
- const DouyinEmojiProParamsSchema = zod.default.object({ methodType: zod.default.literal("动态表情数据", { error: "方法类型必须是\"动态表情数据\"" }) });
1233
- const DouyinDanmakuParamsSchema = zod.default.object({
1234
- methodType: zod.default.literal("弹幕数据", { error: "方法类型必须是\"弹幕数据\"" }),
1235
- aweme_id: zod.default.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" }),
1236
- start_time: zod.default.coerce.number({ error: "开始时间必须是数字" }).int({ error: "开始时间必须是整数" }).min(0, { error: "开始时间不能小于0" }).optional(),
1237
- end_time: zod.default.coerce.number({ error: "结束时间必须是数字" }).int({ error: "结束时间必须是整数" }).min(0, { error: "结束时间不能小于0" }).optional(),
1238
- duration: zod.default.coerce.number({ error: "视频时长必须是数字" }).int({ error: "视频时长必须是整数" }).min(0, { error: "视频时长不能小于0" })
1239
- }).refine((data$1) => {
1240
- if (data$1.end_time !== void 0) return data$1.end_time <= data$1.duration;
1241
- return true;
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
- const DouyinValidationSchemas = {
1253
- 文字作品数据: DouyinWorkParamsSchema,
1254
- 聚合解析: DouyinWorkParamsSchema,
1255
- 视频作品数据: DouyinWorkParamsSchema,
1256
- 图集作品数据: DouyinWorkParamsSchema,
1257
- 合辑作品数据: DouyinWorkParamsSchema,
1258
- 评论数据: DouyinCommentParamsSchema,
1259
- 用户主页数据: DouyinUserParamsSchema,
1260
- 用户主页视频列表数据: DouyinUserParamsSchema,
1261
- 热点词数据: DouyinHotWordsParamsSchema,
1262
- 搜索数据: DouyinSearchParamsSchema,
1263
- 音乐数据: DouyinMusicParamsSchema,
1264
- 直播间信息数据: DouyinLiveRoomParamsSchema,
1265
- 申请二维码数据: DouyinQrcodeParamsSchema,
1266
- Emoji数据: DouyinEmojiListParamsSchema,
1267
- 动态表情数据: DouyinEmojiProParamsSchema,
1268
- 指定评论回复数据: DouyinCommentReplyParamsSchema,
1269
- 弹幕数据: DouyinDanmakuParamsSchema
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
- const DouyinMethodRoutes = {
1272
- 聚合解析: "/fetch_one_work",
1273
- 文字作品数据: "/fetch_one_work",
1274
- 视频作品数据: "/fetch_one_work",
1275
- 图集作品数据: "/fetch_one_work",
1276
- 合辑作品数据: "/fetch_one_work",
1277
- 评论数据: "/fetch_work_comments",
1278
- 用户主页数据: "/fetch_user_info",
1279
- 用户主页视频列表数据: "/fetch_user_post_videos",
1280
- 搜索数据: "/fetch_search_info",
1281
- 热点词数据: "/fetch_suggest_words",
1282
- 音乐数据: "/fetch_music_work",
1283
- Emoji数据: "/fetch_emoji_list",
1284
- 动态表情数据: "/fetch_emoji_pro_list",
1285
- 直播间信息数据: "/fetch_user_live_videos",
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/validation/kuaishou.ts
1292
- const KuaishouVideoParamsSchema = zod.default.object({
1293
- methodType: zod.default.literal("单个视频作品数据", { error: "方法类型必须是\"单个视频作品数据\"" }),
1294
- photoId: zod.default.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" })
1295
- });
1296
- const KuaishouCommentParamsSchema = zod.default.object({
1297
- methodType: zod.default.literal("评论数据", { error: "方法类型必须是\"评论数据\"" }),
1298
- photoId: zod.default.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" })
1299
- });
1300
- const KuaishouEmojiParamsSchema = zod.default.object({ methodType: zod.default.literal("Emoji数据", { error: "方法类型必须是\"Emoji数据\"" }) });
1301
- const KuaishouValidationSchemas = {
1302
- 单个视频作品数据: KuaishouVideoParamsSchema,
1303
- 评论数据: KuaishouCommentParamsSchema,
1304
- Emoji数据: KuaishouEmojiParamsSchema
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
- const KuaishouMethodRoutes = {
1307
- 单个视频作品数据: "/fetch_one_work",
1308
- 评论数据: "/fetch_work_comments",
1309
- Emoji数据: "/fetch_emoji_list"
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
- var xiaohongshuSign = class {
1318
- static client = new __ikenxuan_xhshow_ts.Xhshow();
1319
- /**
1320
- * 生成GET请求的X-S签名
1321
- * @param path - API路径
1322
- * @param a1Cookie - a1 cookie值
1323
- * @param clientType - 客户端类型,默认为 'xhs-pc-web'
1324
- * @param params - 查询参数对象
1325
- * @returns X-S签名
1326
- */
1327
- static generateXSGet(path$1, a1Cookie, clientType = "xhs-pc-web", params = {}) {
1328
- return this.client.signXsGet(path$1, a1Cookie, clientType, params);
1329
- }
1330
- /**
1331
- * 生成POST请求的X-S签名
1332
- * @param path - API路径
1333
- * @param a1Cookie - a1 cookie值
1334
- * @param clientType - 客户端类型,默认为 'xhs-pc-web'
1335
- * @param body - 请求体对象
1336
- * @returns X-S签名
1337
- */
1338
- static generateXSPost(path$1, a1Cookie, clientType = "xhs-pc-web", body = {}) {
1339
- return this.client.signXsPost(path$1, a1Cookie, clientType, body);
1340
- }
1341
- /**
1342
- * 生成X-S签名(兼容旧接口)
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
- } catch (error) {
1362
- console.error("生成X-S签名失败:", error);
1363
- throw new Error(`签名生成失败: ${error}`);
1190
+ logger.error("网络请求失败:", error.message);
1191
+ return createNetworkErrorResult(error, attempt);
1364
1192
  }
1193
+ throw error;
1365
1194
  }
1366
- /**
1367
- * 生成X-S-Common参数
1368
- * @param length - 长度
1369
- * @returns Base64编码的随机字符串
1370
- */
1371
- static generateXSCommon(length = 945) {
1372
- return node_crypto.default.randomBytes(length).toString("base64").replace(/=+$/, "");
1373
- }
1374
- /**
1375
- * 生成X-T时间戳
1376
- * @returns 当前时间戳字符串
1377
- */
1378
- static generateXT() {
1379
- return Date.now().toString();
1380
- }
1381
- /**
1382
- * 生成X-B3-Traceid
1383
- * @returns 16位随机字符串
1384
- */
1385
- static generateXB3Traceid() {
1386
- return Array.from({ length: 16 }, () => "abcdef0123456789"[Math.floor(Math.random() * 16)]).join("");
1387
- }
1388
- /**
1389
- * 从cookie字符串中提取a1值
1390
- * @param cookieString - 完整的cookie字符串
1391
- * @returns a1 cookie值
1392
- */
1393
- static extractA1FromCookie(cookieString) {
1394
- const match = cookieString.match(/a1=([^;]+)/);
1395
- return match ? match[1] : "";
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
- * 生成搜索ID
1399
- * @returns 搜索ID字符串
1400
- */
1401
- static getSearchId = () => (BigInt(Date.now()) << 64n) + BigInt(Math.floor(Math.random() * 2147483646)).toString(36);
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/xiaohongshu/API.ts
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
- let SearchSortType = /* @__PURE__ */ function(SearchSortType$1) {
1410
- /**
1411
- * 默认排序
1412
- */
1413
- SearchSortType$1["GENERAL"] = "general";
1414
- /**
1415
- * 最受欢迎(按热度降序)
1416
- */
1417
- SearchSortType$1["MOST_POPULAR"] = "popularity_descending";
1418
- /**
1419
- * 最新发布(按时间降序)
1420
- */
1421
- SearchSortType$1["LATEST"] = "time_descending";
1422
- return SearchSortType$1;
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
- let SearchNoteType = /* @__PURE__ */ function(SearchNoteType$1) {
1428
- /**
1429
- * 默认(全部类型)
1430
- */
1431
- SearchNoteType$1[SearchNoteType$1["ALL"] = 0] = "ALL";
1432
- /**
1433
- * 仅视频
1434
- */
1435
- SearchNoteType$1[SearchNoteType$1["VIDEO"] = 1] = "VIDEO";
1436
- /**
1437
- * 仅图片
1438
- */
1439
- SearchNoteType$1[SearchNoteType$1["IMAGE"] = 2] = "IMAGE";
1440
- return SearchNoteType$1;
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 params - 参数对象
1445
- * @returns 查询字符串
1334
+ * 快手平台默认请求配置
1335
+ * @param cookie - 用户Cookie
1336
+ * @param requestConfig - 外部请求配置(优先级最高)
1337
+ * @returns 合并后的请求配置
1446
1338
  */
1447
- const buildQueryString$1 = (params) => {
1448
- return Object.entries(params).filter(([_, value]) => value !== void 0 && value !== null).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
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
- * 小红书API地址配置
1362
+ * 获取小红书默认配置
1363
+ * @param cookie - 用户Cookie
1364
+ * @returns 小红书请求配置
1452
1365
  */
1453
- const xiaohongshuApiUrls = {
1454
- 首页推荐数据(data$1 = {}) {
1455
- return {
1456
- apiPath: "/api/sns/web/v1/homefeed",
1457
- Url: "https://edith.xiaohongshu.com/api/sns/web/v1/homefeed",
1458
- Body: {
1459
- cursor_score: data$1.cursor_score ?? "1.7599348899670024E9",
1460
- num: data$1.num ?? 33,
1461
- refresh_type: data$1.refresh_type ?? 3,
1462
- note_index: data$1.note_index ?? 33,
1463
- category: data$1.category ?? "homefeed_recommend",
1464
- search_key: data$1.search_key ?? "",
1465
- image_formats: [
1466
- "jpg",
1467
- "webp",
1468
- "avif"
1469
- ]
1470
- }
1471
- };
1472
- },
1473
- 单个笔记数据(data$1) {
1474
- return {
1475
- apiPath: "/api/sns/web/v1/feed",
1476
- Url: "https://edith.xiaohongshu.com/api/sns/web/v1/feed",
1477
- Body: {
1478
- source_note_id: data$1.note_id,
1479
- image_formats: [
1480
- "jpg",
1481
- "webp",
1482
- "avif"
1483
- ],
1484
- extra: { need_body_topic: "1" },
1485
- xsec_source: "pc_feed",
1486
- xsec_token: data$1.xsec_token
1487
- }
1488
- };
1489
- },
1490
- 评论数据(data$1) {
1491
- return {
1492
- apiPath: "/api/sns/web/v2/comment/page",
1493
- Url: `https://edith.xiaohongshu.com/api/sns/web/v2/comment/page?${buildQueryString$1({
1494
- note_id: data$1.note_id,
1495
- cursor: data$1.cursor ?? "",
1496
- image_formats: [
1497
- "jpg",
1498
- "webp",
1499
- "avif"
1500
- ].join(","),
1501
- xsec_token: data$1.xsec_token
1502
- })}`
1503
- };
1504
- },
1505
- 用户数据(data$1) {
1506
- return {
1507
- apiPath: "/api/sns/web/v1/user/otherinfo",
1508
- Url: `https://www.xiaohongshu.com/user/profile/${data$1.user_id}`
1509
- };
1510
- },
1511
- 用户笔记数据(data$1) {
1512
- return {
1513
- apiPath: "/api/sns/web/v1/user_posted",
1514
- Url: `https://edith.xiaohongshu.com/api/sns/web/v1/user_posted?${buildQueryString$1({
1515
- user_id: data$1.user_id,
1516
- cursor: data$1.cursor ?? "",
1517
- num: data$1.num ?? 30,
1518
- image_formats: [
1519
- "jpg",
1520
- "webp",
1521
- "avif"
1522
- ].join(","),
1523
- xsec_source: "pc_feed"
1524
- })}`
1525
- };
1526
- },
1527
- 表情列表(data$1) {
1528
- return {
1529
- apiPath: "/api/im/redmoji/detail",
1530
- Url: "https://edith.xiaohongshu.com/api/im/redmoji/detail"
1531
- };
1532
- },
1533
- 搜索笔记(data$1) {
1534
- return {
1535
- apiPath: "/api/sns/web/v1/search/notes",
1536
- Body: {
1537
- keyword: data$1.keyword,
1538
- page: data$1.page ?? 1,
1539
- page_size: data$1.page_size ?? 20,
1540
- sort: SearchSortType.GENERAL,
1541
- note_type: SearchNoteType.ALL,
1542
- search_id: xiaohongshuSign.getSearchId(),
1543
- image_formats: [
1544
- "jpg",
1545
- "webp",
1546
- "avif"
1547
- ]
1548
- },
1549
- Url: "https://edith.xiaohongshu.com/api/sns/web/v1/search/notes"
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
- * 创建小红书API URLs实例
1555
- * @returns 小红书API URLs对象
1476
+ * 创建B站API方法的通用工厂函数
1477
+ * @template T - B站方法类型键名
1478
+ * @param methodType - 方法类型
1479
+ * @returns 返回配置好的API方法
1556
1480
  */
1557
- const createXiaohongshuApiUrls = () => {
1558
- return xiaohongshuApiUrls;
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 HomeFeedParamsSchema = zod.default.object({
1569
- methodType: zod.default.literal("首页推荐数据", { error: "方法类型必须是\"首页推荐数据\"" }),
1570
- cursor_score: zod.default.string({ error: "cursor_score必须是字符串" }).optional(),
1571
- num: zod.default.coerce.number({ error: "数量必须是数字" }).int({ error: "数量必须是整数" }).min(1, { error: "数量不能小于1" }).max(100, { error: "数量不能大于100" }).optional(),
1572
- refresh_type: zod.default.coerce.number({ error: "refresh_type必须是数字" }).int({ error: "refresh_type必须是整数" }).optional(),
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 NoteParamsSchema = zod.default.object({
1581
- methodType: zod.default.literal("单个笔记数据", { error: "方法类型必须是\"单个笔记数据\"" }),
1582
- note_id: zod.default.string({ error: "note_id必须是字符串" }),
1583
- xsec_token: zod.default.string({ error: "xsec_token必须是字符串" })
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 CommentParamsSchema = zod.default.object({
1589
- methodType: zod.default.literal("评论数据", { error: "方法类型必须是\"评论数据\"" }),
1590
- note_id: zod.default.string({ error: "note_id必须是字符串" }),
1591
- cursor: zod.default.string({ error: "cursor必须是字符串" }).optional(),
1592
- xsec_token: zod.default.string({ error: "xsec_token必须是字符串" })
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 UserParamsSchema = zod.default.object({
1598
- methodType: zod.default.literal("用户数据", { error: "方法类型必须是\"用户数据\"" }),
1599
- user_id: zod.default.string({ error: "user_id必须是字符串" })
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 UserNoteParamsSchema = zod.default.object({
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 SearchNoteParamsSchema = zod.default.object({
1615
- methodType: zod.default.literal("搜索笔记", { error: "方法类型必须是\"搜索笔记\"" }),
1616
- keyword: zod.default.string({ error: "keyword必须是字符串" }),
1617
- page: zod.default.coerce.number({ error: "page必须是数字" }).int({ error: "page必须是整数" }).min(1, { error: "page不能小于1" }).optional(),
1618
- 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(),
1619
- sort: zod.default.enum(SearchSortTypeValues, { error: "排序类型不合法" }).optional(),
1620
- note_type: zod.default.coerce.number({ error: "笔记类型必须是数字" }).int({ error: "笔记类型必须是整数" }).refine((val) => SearchNoteTypeValues.includes(val), { message: "笔记类型不合法" }).optional()
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 XiaohongshuValidationSchemas = {
1626
- 首页推荐数据: HomeFeedParamsSchema,
1627
- 单个笔记数据: NoteParamsSchema,
1628
- 评论数据: CommentParamsSchema,
1629
- 用户数据: UserParamsSchema,
1630
- 用户笔记数据: UserNoteParamsSchema,
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 XiaohongshuMethodRoutes = {
1638
- 首页推荐数据: "/fetch_home_feed",
1639
- 单个笔记数据: "/fetch_one_note",
1640
- 评论数据: "/fetch_note_comments",
1641
- 用户数据: "/fetch_user_profile",
1642
- 用户笔记数据: "/fetch_user_notes",
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/validation/index.ts
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
- const validateKuaishouParams = (methodType, params) => {
1686
- return KuaishouValidationSchemas[methodType].parse(typeof params === "object" && params !== null ? {
1687
- methodType,
1688
- ...params
1689
- } : {
1690
- methodType,
1691
- params
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
- const validateXiaohongshuParams = (methodType, params) => {
1701
- return XiaohongshuValidationSchemas[methodType].parse(typeof params === "object" && params !== null ? {
1702
- methodType,
1703
- ...params
1704
- } : {
1705
- methodType,
1706
- params
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 data - 响应数据
1712
- * @param message - 响应消息(可选)
1713
- * @param code - 响应状态码(可选,默认200)
1714
- * @returns 格式化的成功API响应对象
1793
+ * 处理错误并返回统一格式
1794
+ * @param error - 错误对象
1795
+ * @param requestPath - HTTP请求路径(可选)
1796
+ * @returns 统一的错误响应格式
1715
1797
  */
1716
- const createSuccessResponse = (data$1, message, code = 200) => {
1717
- return {
1718
- success: true,
1719
- data: data$1,
1720
- message,
1721
- code,
1722
- error: void 0
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
- * @param error - 错误信息
1728
- * @param message - 详细错误消息(可选)
1729
- * @param code - 错误状态码(可选,默认500)
1730
- * @returns 格式化的错误响应对象
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
- success: false,
1735
- error,
1736
- message,
1737
- code,
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.10.0";
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;