@ikenxuan/amagi 5.6.3 → 5.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4725 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
2
+ const require_chunk = require('../chunk-CUT6urMc.cjs');
3
+ let chalk = require("chalk");
4
+ chalk = require_chunk.__toESM(chalk);
5
+ let log4js = require("log4js");
6
+ log4js = require_chunk.__toESM(log4js);
7
+ let node_path = require("node:path");
8
+ node_path = require_chunk.__toESM(node_path);
9
+ let node_url = require("node:url");
10
+ node_url = require_chunk.__toESM(node_url);
11
+ let node_fs = require("node:fs");
12
+ node_fs = require_chunk.__toESM(node_fs);
13
+ let axios = require("axios");
14
+ axios = require_chunk.__toESM(axios);
15
+ let node_crypto = require("node:crypto");
16
+ node_crypto = require_chunk.__toESM(node_crypto);
17
+ let zod = require("zod");
18
+ zod = require_chunk.__toESM(zod);
19
+ let __ikenxuan_xhshow_ts = require("@ikenxuan/xhshow-ts");
20
+ __ikenxuan_xhshow_ts = require_chunk.__toESM(__ikenxuan_xhshow_ts);
21
+ let express = require("express");
22
+ express = require_chunk.__toESM(express);
23
+
24
+ //#region src/model/logger.ts
25
+ /** 获取包的绝对路径 */
26
+ const getPackageLogsPath = () => {
27
+ const currentFileUrl = require("url").pathToFileURL(__filename).href;
28
+ const currentFilePath = (0, node_url.fileURLToPath)(currentFileUrl);
29
+ let packageRoot = node_path.default.dirname(currentFilePath);
30
+ while (packageRoot !== node_path.default.dirname(packageRoot)) {
31
+ if (node_fs.default.existsSync(node_path.default.join(packageRoot, "package.json"))) break;
32
+ packageRoot = node_path.default.dirname(packageRoot);
33
+ }
34
+ return node_path.default.join(packageRoot, "logs");
35
+ };
36
+ const logsPath = getPackageLogsPath();
37
+ /** 获取日志级别,优先使用环境变量,默认为 info */
38
+ const getLogLevel = () => {
39
+ return process.env.LOG_LEVEL || "info";
40
+ };
41
+ const currentLogLevel = getLogLevel();
42
+ log4js.default.configure({
43
+ appenders: {
44
+ console: {
45
+ type: "stdout",
46
+ layout: {
47
+ type: "pattern",
48
+ pattern: "%[[amagi][%d{hh:mm:ss.SSS}][%4.4p]%] %m"
49
+ }
50
+ },
51
+ command: {
52
+ type: "dateFile",
53
+ filename: node_path.default.join(logsPath, "application", "command"),
54
+ pattern: "yyyy-MM-dd.log",
55
+ numBackups: 15,
56
+ alwaysIncludePattern: true,
57
+ layout: {
58
+ type: "pattern",
59
+ pattern: "[%d{hh:mm:ss.SSS}][%4.4p] %m"
60
+ }
61
+ },
62
+ httpConsole: {
63
+ type: "stdout",
64
+ layout: {
65
+ type: "pattern",
66
+ pattern: "%[[amagi][%d{hh:mm:ss.SSS}][HTTP]%] %m"
67
+ }
68
+ },
69
+ httpRequest: {
70
+ type: "dateFile",
71
+ filename: node_path.default.join(logsPath, "http", "requests"),
72
+ pattern: "yyyy-MM-dd.log",
73
+ numBackups: 30,
74
+ alwaysIncludePattern: true,
75
+ layout: {
76
+ type: "pattern",
77
+ pattern: "[%d{hh:mm:ss.SSS}][%4.4p] %m"
78
+ }
79
+ }
80
+ },
81
+ categories: {
82
+ default: {
83
+ appenders: ["console", "command"],
84
+ level: currentLogLevel
85
+ },
86
+ http: {
87
+ appenders: ["httpConsole", "httpRequest"],
88
+ level: "debug"
89
+ }
90
+ },
91
+ pm2: true
92
+ });
93
+ var CustomLogger = class {
94
+ logger;
95
+ chalk;
96
+ red;
97
+ green;
98
+ yellow;
99
+ blue;
100
+ magenta;
101
+ cyan;
102
+ white;
103
+ gray;
104
+ constructor(name) {
105
+ this.logger = log4js.default.getLogger(name);
106
+ this.chalk = new chalk.Chalk();
107
+ this.red = this.chalk.red;
108
+ this.green = this.chalk.green;
109
+ this.yellow = this.chalk.yellow;
110
+ this.blue = this.chalk.blue;
111
+ this.magenta = this.chalk.magenta;
112
+ this.cyan = this.chalk.cyan;
113
+ this.white = this.chalk.white;
114
+ this.gray = this.chalk.gray;
115
+ }
116
+ info(message, ...args) {
117
+ this.logger.info(message, ...args);
118
+ }
119
+ warn(message, ...args) {
120
+ this.logger.warn(message, ...args);
121
+ }
122
+ error(message, ...args) {
123
+ this.logger.error(message, ...args);
124
+ }
125
+ mark(message, ...args) {
126
+ this.logger.mark(message, ...args);
127
+ }
128
+ debug(message, ...args) {
129
+ this.logger.debug(message, ...args);
130
+ }
131
+ };
132
+ const logger = new CustomLogger("default");
133
+ const httpLogger = new CustomLogger("http");
134
+ /**
135
+ * 创建一个日志中间件,用于记录特定请求的详细信息
136
+ * @param pathsToLog 指定需要记录日志的请求路径数组如果未提供,则记录所有请求的日志
137
+ * @returns
138
+ */
139
+ const logMiddleware = (pathsToLog) => {
140
+ return (req, res, next) => {
141
+ if (!pathsToLog || pathsToLog.some((path$1) => req.url.startsWith(path$1))) {
142
+ const startTime = Date.now();
143
+ const url = req.url;
144
+ const method = req.method;
145
+ const clientIP = req.headers["x-forwarded-for"] || req.socket.remoteAddress;
146
+ const referer = req.headers["referer"] || req.headers["referrer"] || "-";
147
+ const contentType = req.headers["content-type"] || "-";
148
+ const requestSize = req.headers["content-length"] || "0";
149
+ const protocol = req.protocol;
150
+ const httpVersion = req.httpVersion;
151
+ res.on("finish", () => {
152
+ const responseTime = Date.now() - startTime;
153
+ const statusCode = res.statusCode;
154
+ const responseSize = res.get("content-length") || "0";
155
+ const logData = {
156
+ method,
157
+ url,
158
+ statusCode,
159
+ responseTime: `${responseTime}ms`,
160
+ clientIP,
161
+ referer,
162
+ contentType,
163
+ requestSize: `${requestSize}B`,
164
+ responseSize: `${responseSize}B`,
165
+ protocol,
166
+ httpVersion,
167
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
168
+ };
169
+ httpLogger.debug(JSON.stringify(logData));
170
+ });
171
+ }
172
+ next();
173
+ };
174
+ };
175
+
176
+ //#endregion
177
+ //#region src/model/networks.ts
178
+ /**
179
+ * 清理User-Agent中的Edge标识,确保请求兼容性
180
+ * @param userAgent - 原始User-Agent字符串
181
+ * @returns 清理后的User-Agent字符串
182
+ */
183
+ const cleanUserAgent = (userAgent) => {
184
+ return userAgent.replace(/\s+Edg\/[\d\.]+/g, "");
185
+ };
186
+ /**
187
+ * 执行网络请求并返回数据
188
+ * @param config - axios请求配置
189
+ * @returns 响应数据
190
+ */
191
+ const fetchData = async (config) => {
192
+ try {
193
+ const cleanedConfig = { ...config };
194
+ if (cleanedConfig.headers && cleanedConfig.headers["User-Agent"]) cleanedConfig.headers["User-Agent"] = cleanUserAgent(cleanedConfig.headers["User-Agent"]);
195
+ return (await (0, axios.default)({
196
+ ...cleanedConfig,
197
+ validateStatus: () => true
198
+ })).data;
199
+ } catch (error) {
200
+ if (error instanceof axios.AxiosError) {
201
+ logger.error("网络请求失败:", error.message);
202
+ throw error;
203
+ }
204
+ throw error;
205
+ }
206
+ };
207
+ const normalizeHeaders = (headers) => {
208
+ if (headers && typeof headers.toJSON === "function") return headers.toJSON();
209
+ return headers || {};
210
+ };
211
+ const fetchResponse = async (config) => {
212
+ try {
213
+ const cleanedConfig = { ...config };
214
+ if (cleanedConfig.headers && cleanedConfig.headers["User-Agent"]) cleanedConfig.headers["User-Agent"] = cleanUserAgent(cleanedConfig.headers["User-Agent"]);
215
+ return await (0, axios.default)({
216
+ ...cleanedConfig,
217
+ validateStatus: () => true
218
+ });
219
+ } catch (error) {
220
+ if (error instanceof axios.AxiosError) throw error;
221
+ throw new Error("网络请求失败");
222
+ }
223
+ };
224
+ /**
225
+ * 获取响应头和数据
226
+ * @param config - axios请求配置
227
+ * @returns 包含headers和data的对象
228
+ */
229
+ const getHeadersAndData = async (config) => {
230
+ try {
231
+ const response = await fetchResponse(config);
232
+ return {
233
+ headers: normalizeHeaders(response.headers),
234
+ data: response.data
235
+ };
236
+ } catch (error) {
237
+ logger.error("获取响应头和数据失败:", error);
238
+ return {
239
+ headers: {},
240
+ data: {}
241
+ };
242
+ }
243
+ };
244
+
245
+ //#endregion
246
+ //#region src/platform/bilibili/qtparam.ts
247
+ /**
248
+ * 生成B站视频流请求参数
249
+ * @param BASEURL - 基础请求URL
250
+ * @param cookie - 用户Cookie
251
+ * @returns 包含查询参数、登录状态和VIP状态的对象
252
+ */
253
+ const qtparam = async (BASEURL, cookie) => {
254
+ if (cookie === "") return {
255
+ QUERY: "&platform=html5",
256
+ STATUS: "!isLogin"
257
+ };
258
+ const logininfo = await fetchData({
259
+ url: bilibiliApiUrls.登录基本信息(),
260
+ headers: { Cookie: cookie }
261
+ });
262
+ const sign = await wbi_sign(BASEURL, cookie);
263
+ const qn = [
264
+ 6,
265
+ 16,
266
+ 32,
267
+ 64,
268
+ 74,
269
+ 80,
270
+ 112,
271
+ 116,
272
+ 120,
273
+ 125,
274
+ 126,
275
+ 127
276
+ ];
277
+ let isvip;
278
+ logininfo.data.vipStatus === 1 ? isvip = true : isvip = false;
279
+ if (isvip) return {
280
+ QUERY: `&fnval=4048&fourk=1&${sign}`,
281
+ STATUS: "isLogin",
282
+ isvip
283
+ };
284
+ else return {
285
+ QUERY: `&qn=${qn[3]}&fnval=16&${sign}`,
286
+ STATUS: "isLogin",
287
+ isvip
288
+ };
289
+ };
290
+
291
+ //#endregion
292
+ //#region src/platform/bilibili/sign/bv2av.ts
293
+ const XOR_CODE = 23442827791579n;
294
+ const MASK_CODE = 2251799813685247n;
295
+ const MAX_AID = 1n << 51n;
296
+ const BASE = 58n;
297
+ const data = "FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf";
298
+ /**
299
+ * av号转bv号
300
+ * @param aid av号
301
+ * @returns
302
+ */
303
+ const av2bv = (aid) => {
304
+ const bytes = [
305
+ "B",
306
+ "V",
307
+ "1",
308
+ "0",
309
+ "0",
310
+ "0",
311
+ "0",
312
+ "0",
313
+ "0",
314
+ "0",
315
+ "0",
316
+ "0"
317
+ ];
318
+ let bvIndex = bytes.length - 1;
319
+ let tmp = (MAX_AID | BigInt(aid)) ^ XOR_CODE;
320
+ while (tmp > 0) {
321
+ bytes[bvIndex] = data[Number(tmp % BigInt(BASE))];
322
+ tmp = tmp / BASE;
323
+ bvIndex -= 1;
324
+ }
325
+ [bytes[3], bytes[9]] = [bytes[9], bytes[3]];
326
+ [bytes[4], bytes[7]] = [bytes[7], bytes[4]];
327
+ return bytes.join("");
328
+ };
329
+ /**
330
+ * bv号转av号
331
+ * @param bvid bv号
332
+ * @returns
333
+ */
334
+ const bv2av = (bvid) => {
335
+ const bvidArr = Array.from(bvid);
336
+ [bvidArr[3], bvidArr[9]] = [bvidArr[9], bvidArr[3]];
337
+ [bvidArr[4], bvidArr[7]] = [bvidArr[7], bvidArr[4]];
338
+ bvidArr.splice(0, 3);
339
+ const tmp = bvidArr.reduce((pre, bvidChar) => pre * BASE + BigInt(data.indexOf(bvidChar)), 0n);
340
+ return Number(tmp & MASK_CODE ^ XOR_CODE);
341
+ };
342
+
343
+ //#endregion
344
+ //#region src/platform/bilibili/API.ts
345
+ var BiLiBiLiAPI = class {
346
+ 登录基本信息() {
347
+ return "https://api.bilibili.com/x/web-interface/nav";
348
+ }
349
+ 视频详细信息(data$1) {
350
+ return `https://api.bilibili.com/x/web-interface/view?bvid=${data$1.bvid}`;
351
+ }
352
+ 视频流信息(data$1) {
353
+ return `https://api.bilibili.com/x/player/playurl?avid=${data$1.avid}&cid=${data$1.cid}`;
354
+ }
355
+ /** 评论区类型,type参数详见 [评论区类型代码](https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/comment/readme.md#评论区类型代码) */
356
+ 评论区明细(data$1) {
357
+ const params = new URLSearchParams({
358
+ oid: data$1.oid.toString(),
359
+ type: data$1.type.toString(),
360
+ mode: (data$1.mode ?? 3).toString(),
361
+ plat: "1",
362
+ seek_rpid: "",
363
+ web_location: "1315875"
364
+ });
365
+ if (data$1.pagination_str) params.append("pagination_str", JSON.stringify({ offset: data$1.pagination_str }));
366
+ else params.append("pagination_str", JSON.stringify({ offset: "" }));
367
+ return `https://api.bilibili.com/x/v2/reply/wbi/main?${params.toString()}`;
368
+ }
369
+ 评论区状态(data$1) {
370
+ return `https://api.bilibili.com/x/v2/reply/subject/description?type=${data$1.type}&oid=${data$1.oid}`;
371
+ }
372
+ 表情列表() {
373
+ return "https://api.bilibili.com/x/emote/user/panel/web?business=reply&web_location=0.0";
374
+ }
375
+ 番剧明细(data$1) {
376
+ if (data$1.ep_id) return `https://api.bilibili.com/pgc/view/web/season?ep_id=${data$1.ep_id}`;
377
+ else if (data$1.season_id) return `https://api.bilibili.com/pgc/view/web/season?season_id=${data$1.season_id}`;
378
+ else throw new Error("拟造接口地址出错,缺少 ep_id 或 season_id 参数");
379
+ }
380
+ 番剧视频流信息(data$1) {
381
+ return `https://api.bilibili.com/pgc/player/web/playurl?cid=${data$1.cid}&ep_id=${data$1.ep_id}`;
382
+ }
383
+ 用户空间动态(data$1) {
384
+ return `https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space?host_mid=${data$1.host_mid}&features=itemOpusStyle,listOnlyfans,opusBigCover,onlyfansVote,forwardListHidden,decorationCard,commentsNewVersion,onlyfansAssetsV2,ugcDelete,onlyfansQaCard`;
385
+ }
386
+ 动态详情(data$1) {
387
+ 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`;
388
+ }
389
+ 动态卡片信息(data$1) {
390
+ return `https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/get_dynamic_detail?dynamic_id=${data$1.dynamic_id}`;
391
+ }
392
+ 用户名片信息(data$1) {
393
+ return `https://api.bilibili.com/x/web-interface/card?mid=${data$1.host_mid}&photo=true`;
394
+ }
395
+ 直播间信息(data$1) {
396
+ return `https://api.live.bilibili.com/room/v1/Room/get_info?room_id=${data$1.room_id}`;
397
+ }
398
+ 直播间初始化信息(data$1) {
399
+ return `https://api.live.bilibili.com/room/v1/Room/room_init?id=${data$1.room_id}`;
400
+ }
401
+ 申请二维码() {
402
+ return "https://passport.bilibili.com/x/passport-login/web/qrcode/generate";
403
+ }
404
+ 二维码状态(data$1) {
405
+ return `https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key=${data$1.qrcode_key}`;
406
+ }
407
+ 获取UP主总播放量(data$1) {
408
+ return `https://api.bilibili.com/x/space/upstat?mid=${data$1.host_mid}`;
409
+ }
410
+ 专栏正文内容(data$1) {
411
+ return `https://api.bilibili.com/x/article/view?id=${data$1.id}`;
412
+ }
413
+ 专栏显示卡片信息(data$1) {
414
+ return `https://api.bilibili.com/x/article/cards?ids=${Array.isArray(data$1.ids) ? data$1.ids.join(",") : data$1.ids}`;
415
+ }
416
+ 专栏文章基本信息(data$1) {
417
+ return `https://api.bilibili.com/x/article/viewinfo?id=${data$1.id}`;
418
+ }
419
+ 文集基本信息(data$1) {
420
+ return `https://api.bilibili.com/x/article/list/web/articles?id=${data$1.id}`;
421
+ }
422
+ };
423
+ /** 该类下的所有方法只会返回拼接好参数后的 Url 地址,需要手动请求该地址以获取数据 */
424
+ const bilibiliApiUrls = new BiLiBiLiAPI();
425
+
426
+ //#endregion
427
+ //#region src/types/NetworksConfigType.ts
428
+ /** 未知错误 */
429
+ let amagiAPIErrorCode = /* @__PURE__ */ function(amagiAPIErrorCode$1) {
430
+ /** 未知错误 */
431
+ amagiAPIErrorCode$1["UNKNOWN"] = "UNKNOWN_ERROR";
432
+ return amagiAPIErrorCode$1;
433
+ }({});
434
+ /** 抖音平台API错误码 */
435
+ let douoyinAPIErrorCode = /* @__PURE__ */ function(douoyinAPIErrorCode$1) {
436
+ /** Cookie无效或已过期 */
437
+ douoyinAPIErrorCode$1["COOKIE"] = "INVALID_COOKIE";
438
+ /** 内容被隐藏或下架 */
439
+ douoyinAPIErrorCode$1["FILTER"] = "CONTENT_FILTERED";
440
+ /** 当前用户未开播 */
441
+ douoyinAPIErrorCode$1["NOT_LIVE"] = "USER_NOT_LIVE";
442
+ /** 未知错误 */
443
+ douoyinAPIErrorCode$1["UNKNOWN"] = "UNKNOWN_ERROR";
444
+ return douoyinAPIErrorCode$1;
445
+ }({});
446
+ /** B站平台API错误码 */
447
+ let bilibiliAPIErrorCode = /* @__PURE__ */ function(bilibiliAPIErrorCode$1) {
448
+ /** 应用程序不存在或已被封禁 */
449
+ bilibiliAPIErrorCode$1["APP_NOT_FOUND"] = "-1";
450
+ /** Access Key 错误 */
451
+ bilibiliAPIErrorCode$1["ACCESS_KEY_ERROR"] = "-2";
452
+ /** API 校验密匙错误 */
453
+ bilibiliAPIErrorCode$1["API_KEY_ERROR"] = "-3";
454
+ /** 调用方对该Method没有权限 */
455
+ bilibiliAPIErrorCode$1["METHOD_NOT_PERMITTED"] = "-4";
456
+ /** 账号未登录 */
457
+ bilibiliAPIErrorCode$1["NOT_LOGGED_IN"] = "-101";
458
+ /** 账号被封停 */
459
+ bilibiliAPIErrorCode$1["ACCOUNT_BANNED"] = "-102";
460
+ /** 积分不足 */
461
+ bilibiliAPIErrorCode$1["POINTS_INSUFFICIENT"] = "-103";
462
+ /** 硬币不足 */
463
+ bilibiliAPIErrorCode$1["COINS_INSUFFICIENT"] = "-104";
464
+ /** 验证码错误 */
465
+ bilibiliAPIErrorCode$1["CAPTCHA_ERROR"] = "-105";
466
+ /** 账号非正式会员或在适应期 */
467
+ bilibiliAPIErrorCode$1["MEMBERSHIP_LIMITED"] = "-106";
468
+ /** 应用不存在或者被封禁 */
469
+ bilibiliAPIErrorCode$1["APP_BANNED"] = "-107";
470
+ /** 未绑定手机 */
471
+ bilibiliAPIErrorCode$1["PHONE_NOT_BOUND"] = "-108";
472
+ /** 未绑定手机 */
473
+ bilibiliAPIErrorCode$1["PHONE_NOT_BOUND_2"] = "-110";
474
+ /** csrf 校验失败 */
475
+ bilibiliAPIErrorCode$1["CSRF_ERROR"] = "-111";
476
+ /** 系统升级中 */
477
+ bilibiliAPIErrorCode$1["SYSTEM_UPDATING"] = "-112";
478
+ /** 账号尚未实名认证 */
479
+ bilibiliAPIErrorCode$1["NOT_REAL_NAME_VERIFIED"] = "-113";
480
+ /** 请先绑定手机 */
481
+ bilibiliAPIErrorCode$1["NEED_BIND_PHONE"] = "-114";
482
+ /** 请先完成实名认证 */
483
+ bilibiliAPIErrorCode$1["NEED_REAL_NAME_VERIFICATION"] = "-115";
484
+ /** 木有改动 */
485
+ bilibiliAPIErrorCode$1["NO_CHANGE"] = "-304";
486
+ /** 撞车跳转 */
487
+ bilibiliAPIErrorCode$1["CONFLICT_REDIRECT"] = "-307";
488
+ /** 风控校验失败 (UA 或 wbi 参数不合法) */
489
+ bilibiliAPIErrorCode$1["RISK_CONTROL_FAILED"] = "-352";
490
+ /** 请求错误 */
491
+ bilibiliAPIErrorCode$1["BAD_REQUEST"] = "-400";
492
+ /** 未认证 (或非法请求) */
493
+ bilibiliAPIErrorCode$1["UNAUTHORIZED"] = "-401";
494
+ /** 访问权限不足 */
495
+ bilibiliAPIErrorCode$1["FORBIDDEN"] = "-403";
496
+ /** 啥都木有 */
497
+ bilibiliAPIErrorCode$1["NOT_FOUND"] = "-404";
498
+ /** 不支持该方法 */
499
+ bilibiliAPIErrorCode$1["METHOD_NOT_ALLOWED"] = "-405";
500
+ /** 冲突 */
501
+ bilibiliAPIErrorCode$1["CONFLICT"] = "-409";
502
+ /** 请求被拦截 (客户端 ip 被服务端风控) */
503
+ bilibiliAPIErrorCode$1["IP_BLOCKED"] = "-412";
504
+ /** 服务器错误 */
505
+ bilibiliAPIErrorCode$1["SERVER_ERROR"] = "-500";
506
+ /** 过载保护,服务暂不可用 */
507
+ bilibiliAPIErrorCode$1["SERVICE_UNAVAILABLE"] = "-503";
508
+ /** 服务调用超时 */
509
+ bilibiliAPIErrorCode$1["GATEWAY_TIMEOUT"] = "-504";
510
+ /** 超出限制 */
511
+ bilibiliAPIErrorCode$1["RATE_LIMITED"] = "-509";
512
+ /** 上传文件不存在 */
513
+ bilibiliAPIErrorCode$1["FILE_NOT_FOUND"] = "-616";
514
+ /** 上传文件太大 */
515
+ bilibiliAPIErrorCode$1["FILE_TOO_LARGE"] = "-617";
516
+ /** 登录失败次数太多 */
517
+ bilibiliAPIErrorCode$1["LOGIN_ATTEMPTS_EXCEEDED"] = "-625";
518
+ /** 用户不存在 */
519
+ bilibiliAPIErrorCode$1["USER_NOT_FOUND"] = "-626";
520
+ /** 密码太弱 */
521
+ bilibiliAPIErrorCode$1["WEAK_PASSWORD"] = "-628";
522
+ /** 用户名或密码错误 */
523
+ bilibiliAPIErrorCode$1["INVALID_CREDENTIALS"] = "-629";
524
+ /** 操作对象数量限制 */
525
+ bilibiliAPIErrorCode$1["OBJECT_LIMIT_EXCEEDED"] = "-632";
526
+ /** 被锁定 */
527
+ bilibiliAPIErrorCode$1["ACCOUNT_LOCKED"] = "-643";
528
+ /** 用户等级太低 */
529
+ bilibiliAPIErrorCode$1["USER_LEVEL_TOO_LOW"] = "-650";
530
+ /** 重复的用户 */
531
+ bilibiliAPIErrorCode$1["DUPLICATE_USER"] = "-652";
532
+ /** Token 过期 */
533
+ bilibiliAPIErrorCode$1["TOKEN_EXPIRED"] = "-658";
534
+ /** 密码时间戳过期 */
535
+ bilibiliAPIErrorCode$1["PASSWORD_TIMESTAMP_EXPIRED"] = "-662";
536
+ /** 地理区域限制 */
537
+ bilibiliAPIErrorCode$1["GEO_RESTRICTED"] = "-688";
538
+ /** 版权限制 */
539
+ bilibiliAPIErrorCode$1["COPYRIGHT_RESTRICTED"] = "-689";
540
+ /** 扣节操失败 */
541
+ bilibiliAPIErrorCode$1["REPUTATION_DEDUCTION_FAILED"] = "-701";
542
+ /** 请求过于频繁,请稍后再试 */
543
+ bilibiliAPIErrorCode$1["TOO_MANY_REQUESTS"] = "-799";
544
+ /** 服务器开小差了 */
545
+ bilibiliAPIErrorCode$1["SERVER_TEMPORARILY_UNAVAILABLE"] = "-8888";
546
+ /** 未知错误 */
547
+ bilibiliAPIErrorCode$1["UNKNOWN"] = "UNKNOWN";
548
+ return bilibiliAPIErrorCode$1;
549
+ }({});
550
+ /** 快手平台API错误码 */
551
+ let kuaishouAPIErrorCode = /* @__PURE__ */ function(kuaishouAPIErrorCode$1) {
552
+ /** Cookie无效或已过期 */
553
+ kuaishouAPIErrorCode$1["COOKIE"] = "INVALID_COOKIE";
554
+ /** 未知错误 */
555
+ kuaishouAPIErrorCode$1["UNKNOWN"] = "UNKNOWN_ERROR";
556
+ return kuaishouAPIErrorCode$1;
557
+ }({});
558
+ /** 小红书平台API错误码 */
559
+ let xiaohongshuAPIErrorCode = /* @__PURE__ */ function(xiaohongshuAPIErrorCode$1) {
560
+ /** Cookie无效或已过期 */
561
+ xiaohongshuAPIErrorCode$1["COOKIE"] = "INVALID_COOKIE";
562
+ /** 未知错误 */
563
+ xiaohongshuAPIErrorCode$1["UNKNOWN"] = "UNKNOWN_ERROR";
564
+ /** 非法请求 */
565
+ xiaohongshuAPIErrorCode$1[xiaohongshuAPIErrorCode$1["ILLEGAL_REQUEST"] = 500] = "ILLEGAL_REQUEST";
566
+ /** 检测到帐号异常,请稍后重试 */
567
+ xiaohongshuAPIErrorCode$1[xiaohongshuAPIErrorCode$1["ACCOUNT_ABNORMAL"] = 300011] = "ACCOUNT_ABNORMAL";
568
+ /** 网络连接异常,请检查网络设置后重试 */
569
+ xiaohongshuAPIErrorCode$1[xiaohongshuAPIErrorCode$1["NETWORK_ERROR"] = 300012] = "NETWORK_ERROR";
570
+ /** 访问频次异常,请勿频繁操作 */
571
+ xiaohongshuAPIErrorCode$1[xiaohongshuAPIErrorCode$1["FREQUENCY_ERROR"] = 300013] = "FREQUENCY_ERROR";
572
+ /** 浏览器异常,请尝试更换浏览器后重试 */
573
+ xiaohongshuAPIErrorCode$1[xiaohongshuAPIErrorCode$1["BROWSER_ERROR"] = 300015] = "BROWSER_ERROR";
574
+ return xiaohongshuAPIErrorCode$1;
575
+ }({});
576
+
577
+ //#endregion
578
+ //#region src/platform/bilibili/sign/wbi.ts
579
+ /**
580
+ * 混合密钥编码表,用于对 imgKey 和 subKey 进行字符顺序打乱编码
581
+ */
582
+ const mixinKeyEncTab = [
583
+ 46,
584
+ 47,
585
+ 18,
586
+ 2,
587
+ 53,
588
+ 8,
589
+ 23,
590
+ 32,
591
+ 15,
592
+ 50,
593
+ 10,
594
+ 31,
595
+ 58,
596
+ 3,
597
+ 45,
598
+ 35,
599
+ 27,
600
+ 43,
601
+ 5,
602
+ 49,
603
+ 33,
604
+ 9,
605
+ 42,
606
+ 19,
607
+ 29,
608
+ 28,
609
+ 14,
610
+ 39,
611
+ 12,
612
+ 38,
613
+ 41,
614
+ 13,
615
+ 37,
616
+ 48,
617
+ 7,
618
+ 16,
619
+ 24,
620
+ 55,
621
+ 40,
622
+ 61,
623
+ 26,
624
+ 17,
625
+ 0,
626
+ 1,
627
+ 60,
628
+ 51,
629
+ 30,
630
+ 4,
631
+ 22,
632
+ 25,
633
+ 54,
634
+ 21,
635
+ 56,
636
+ 59,
637
+ 6,
638
+ 63,
639
+ 57,
640
+ 62,
641
+ 11,
642
+ 36,
643
+ 20,
644
+ 34,
645
+ 44,
646
+ 52
647
+ ];
648
+ /**
649
+ * 对 imgKey 和 subKey 进行字符顺序打乱编码
650
+ * @param orig - 原始字符串数组,通常是 img_key + sub_key 的字符数组
651
+ * @returns 返回经过编码表打乱后的32位字符串
652
+ */
653
+ const getMixinKey = (orig) => mixinKeyEncTab.map((n) => orig[n]).join("").slice(0, 32);
654
+ /**
655
+ * 为请求参数进行 WBI 签名
656
+ * @param params - 请求参数对象,键值对形式
657
+ * @param img_key - 图片密钥
658
+ * @param sub_key - 子密钥
659
+ * @returns 返回包含时间戳和签名的查询字符串
660
+ */
661
+ const encWbi = (params, img_key, sub_key) => {
662
+ const mixin_key = getMixinKey(img_key + sub_key);
663
+ const curr_time = Math.round(Date.now() / 1e3);
664
+ const chr_filter = /[!'()*]/g;
665
+ Object.assign(params, { wts: curr_time });
666
+ const query = Object.keys(params).sort().map((key) => {
667
+ const value = params[key].toString().replace(chr_filter, "");
668
+ return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
669
+ }).join("&");
670
+ return `&wts=${curr_time}&w_rid=${node_crypto.default.createHash("md5").update(query + mixin_key).digest("hex")}`;
671
+ };
672
+ /**
673
+ * 获取最新的 img_key 和 sub_key
674
+ * @param cookie - 有效的用户 Cookie 字符串
675
+ * @returns 返回包含 img_key 和 sub_key 的对象
676
+ * @throws 当网络请求失败或响应格式不正确时抛出错误
677
+ */
678
+ const getWbiKeys = async (cookie) => {
679
+ const { data: { wbi_img: { img_url, sub_url } } } = (await (0, axios.default)("https://api.bilibili.com/x/web-interface/nav", { headers: { Cookie: cookie } })).data;
680
+ return {
681
+ img_key: img_url.slice(img_url.lastIndexOf("/") + 1, img_url.lastIndexOf(".")),
682
+ sub_key: sub_url.slice(sub_url.lastIndexOf("/") + 1, sub_url.lastIndexOf("."))
683
+ };
684
+ };
685
+ /**
686
+ * 对请求链接进行 WBI 签名
687
+ * @param BASEURL - 完整的请求地址,可以是字符串或 URL 对象
688
+ * @param cookie - 有效的用户 Cookie 字符串
689
+ * @returns 返回包含 WBI 签名的查询字符串
690
+ * @throws 当获取 WBI 密钥失败或 URL 解析失败时抛出错误
691
+ */
692
+ const wbi_sign = async (BASEURL, cookie) => {
693
+ const web_keys = await getWbiKeys(cookie);
694
+ const url = new URL(BASEURL);
695
+ const params = {};
696
+ for (const [key, value] of url.searchParams.entries()) params[key] = value;
697
+ return encWbi(params, web_keys.img_key, web_keys.sub_key);
698
+ };
699
+
700
+ //#endregion
701
+ //#region src/platform/defaultConfigs.ts
702
+ /**
703
+ * 根据User-Agent生成对应的Sec-Ch-Ua值
704
+ * @param userAgent - 用户代理字符串
705
+ * @returns 对应的Sec-Ch-Ua值
706
+ */
707
+ const generateSecChUa = (userAgent) => {
708
+ const chromeMatch = userAgent.match(/Chrome\/(\d+)/);
709
+ const chromeVersion = chromeMatch ? chromeMatch[1] : "125";
710
+ return `"Not)A;Brand";v="8", "Chromium";v="${chromeVersion}", "Google Chrome";v="${chromeVersion}"`;
711
+ };
712
+ /**
713
+ * 抖音平台默认请求配置
714
+ * @param cookie - 用户Cookie
715
+ * @param requestConfig - 外部请求配置(优先级最高)
716
+ * @returns 合并后的请求配置
717
+ */
718
+ const getDouyinDefaultConfig = (cookie, requestConfig) => {
719
+ 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";
720
+ finalUserAgent = finalUserAgent.replace(/\s+Edg\/[\d\.]+/g, "");
721
+ const defHeaders = {
722
+ Accept: "application/json, text/plain, */*",
723
+ "Accept-Encoding": "gzip, deflate, br, zstd",
724
+ "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
725
+ Cookie: cookie ? cookie.replace(/\s+/g, "") : "",
726
+ Priority: "u=1, i",
727
+ Referer: "https://www.douyin.com/",
728
+ "Sec-Ch-Ua": generateSecChUa(finalUserAgent),
729
+ "Sec-Ch-Ua-Mobile": "?0",
730
+ "Sec-Ch-Ua-Platform": "\"Windows\"",
731
+ "Sec-Fetch-Dest": "empty",
732
+ "Sec-Fetch-Mode": "cors",
733
+ "Sec-Fetch-Site": "same-origin",
734
+ "User-Agent": finalUserAgent
735
+ };
736
+ return {
737
+ method: "GET",
738
+ timeout: 1e4,
739
+ ...requestConfig,
740
+ headers: {
741
+ ...defHeaders,
742
+ ...requestConfig?.headers || {}
743
+ }
744
+ };
745
+ };
746
+ /**
747
+ * B站平台默认请求配置
748
+ * @param cookie - 用户Cookie
749
+ * @param requestConfig - 外部请求配置(优先级最高)
750
+ * @returns 合并后的请求配置
751
+ */
752
+ const getBilibiliDefaultConfig = (cookie, requestConfig) => {
753
+ const defHeaders = {
754
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
755
+ "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
756
+ "Cache-Control": "max-age=0",
757
+ Priority: "u=0, i",
758
+ "Sec-Ch-Ua": "\"Microsoft Edge\";v=\"131\", \"Chromium\";v=\"131\", \"Not_A Brand\";v=\"24\"",
759
+ "Sec-Ch-Ua-Mobile": "?0",
760
+ "Sec-Ch-Ua-Platform": "\"Windows\"",
761
+ "Sec-Fetch-Dest": "document",
762
+ "Sec-Fetch-Mode": "navigate",
763
+ "Sec-Fetch-Site": "none",
764
+ "Sec-Fetch-User": "?1",
765
+ "Upgrade-Insecure-Requests": "1",
766
+ Referer: "https://www.bilibili.com/",
767
+ Cookie: cookie ? cookie.replace(/\s+/g, "") : ""
768
+ };
769
+ return {
770
+ method: "GET",
771
+ timeout: 1e4,
772
+ ...requestConfig,
773
+ headers: {
774
+ ...defHeaders,
775
+ ...requestConfig?.headers || {}
776
+ }
777
+ };
778
+ };
779
+ /**
780
+ * 快手平台默认请求配置
781
+ * @param cookie - 用户Cookie
782
+ * @param requestConfig - 外部请求配置(优先级最高)
783
+ * @returns 合并后的请求配置
784
+ */
785
+ const getKuaishouDefaultConfig = (cookie, requestConfig) => {
786
+ const defHeaders = {
787
+ Referer: "https://www.kuaishou.com/new-reco",
788
+ Origin: "https://www.kuaishou.com",
789
+ Accept: "application/json, text/plain, */*",
790
+ "Accept-Encoding": "gzip, deflate, br, zstd",
791
+ "Content-Type": "application/json",
792
+ "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
793
+ Priority: "u=0, i",
794
+ "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",
795
+ Cookie: cookie ? cookie.replace(/\s+/g, "") : ""
796
+ };
797
+ return {
798
+ method: "POST",
799
+ timeout: 1e4,
800
+ ...requestConfig,
801
+ headers: {
802
+ ...defHeaders,
803
+ ...requestConfig?.headers || {}
804
+ }
805
+ };
806
+ };
807
+ /**
808
+ * 获取小红书默认配置
809
+ * @param cookie - 用户Cookie
810
+ * @returns 小红书请求配置
811
+ */
812
+ const getXiaohongshuDefaultConfig = (cookie) => {
813
+ return { headers: {
814
+ "accept": "application/json, text/plain, */*",
815
+ "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
816
+ "cache-control": "no-cache",
817
+ "content-type": "application/json;charset=UTF-8",
818
+ "pragma": "no-cache",
819
+ "priority": "u=1, i",
820
+ "referer": "https://www.xiaohongshu.com/",
821
+ "sec-ch-ua": "\"Microsoft Edge\";v=\"141\", \"Not?A_Brand\";v=\"8\", \"Chromium\";v=\"141\"",
822
+ "sec-ch-ua-mobile": "?0",
823
+ "sec-ch-ua-platform": "\"Windows\"",
824
+ "sec-fetch-dest": "empty",
825
+ "sec-fetch-mode": "cors",
826
+ "sec-fetch-site": "same-site",
827
+ "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",
828
+ "cookie": cookie || ""
829
+ } };
830
+ };
831
+
832
+ //#endregion
833
+ //#region src/platform/bilibili/getdata.ts
834
+ /**
835
+ * B站数据获取函数
836
+ * @param data - 请求数据参数
837
+ * @param cookie - 用户Cookie
838
+ * @param requestConfig - 外部请求配置(优先级最高)
839
+ * @returns 返回B站数据
840
+ */
841
+ const fetchBilibili = async (data$1, cookie, requestConfig) => {
842
+ const defHeaders = getBilibiliDefaultConfig(cookie)["headers"];
843
+ const baseRequestConfig = {
844
+ method: "GET",
845
+ timeout: 1e4,
846
+ ...requestConfig,
847
+ headers: {
848
+ referer: "https://www.bilibili.com/",
849
+ ...defHeaders,
850
+ ...requestConfig?.headers || {}
851
+ }
852
+ };
853
+ switch (data$1.methodType) {
854
+ case "单个视频作品数据": return await GlobalGetData$3(data$1.methodType, {
855
+ ...baseRequestConfig,
856
+ url: bilibiliApiUrls.视频详细信息({ bvid: data$1.bvid })
857
+ });
858
+ case "单个视频下载信息数据": {
859
+ const SIGN = await qtparam(bilibiliApiUrls.视频流信息({
860
+ avid: data$1.avid,
861
+ cid: data$1.cid
862
+ }), baseRequestConfig.headers?.Cookie);
863
+ return await GlobalGetData$3(data$1.methodType, {
864
+ ...baseRequestConfig,
865
+ url: bilibiliApiUrls.视频流信息({
866
+ avid: data$1.avid,
867
+ cid: data$1.cid
868
+ }) + SIGN.QUERY
869
+ });
870
+ }
871
+ case "评论数据": {
872
+ let { oid, number, type, mode, pagination_str, plat, seek_rpid, web_location } = data$1;
873
+ let fetchedComments = [];
874
+ const maxRequestCount = 100;
875
+ let requestCount = 0;
876
+ let tmpresp;
877
+ let nextPaginationStr = pagination_str;
878
+ let isEnd = false;
879
+ const checkStatusUrl = bilibiliApiUrls.评论区状态({
880
+ oid,
881
+ type
882
+ });
883
+ if ((await GlobalGetData$3(data$1.methodType, {
884
+ ...baseRequestConfig,
885
+ url: checkStatusUrl
886
+ })).data === null) {
887
+ logger.error("评论区未开放");
888
+ return {
889
+ code: 404,
890
+ message: "评论区未开放",
891
+ data: null
892
+ };
893
+ }
894
+ while (fetchedComments.length < Number(number ?? 20) && requestCount < maxRequestCount && !isEnd) {
895
+ const baseUrl = bilibiliApiUrls.评论区明细({
896
+ type,
897
+ oid,
898
+ mode: mode ?? 3,
899
+ pagination_str: nextPaginationStr,
900
+ plat: plat ?? 1,
901
+ seek_rpid,
902
+ web_location: web_location ?? "1315875"
903
+ });
904
+ const finalUrl = baseUrl + await wbi_sign(baseUrl, baseRequestConfig.headers?.cookie);
905
+ const response = await GlobalGetData$3(data$1.methodType, {
906
+ ...baseRequestConfig,
907
+ url: finalUrl
908
+ });
909
+ tmpresp = response;
910
+ const currentComments = response.data?.replies || [];
911
+ fetchedComments.push(...currentComments);
912
+ if (response.data?.cursor) {
913
+ nextPaginationStr = response.data.cursor.pagination_reply?.next_offset;
914
+ isEnd = response.data.cursor.is_end;
915
+ } else isEnd = true;
916
+ requestCount++;
917
+ if (isEnd || currentComments.length === 0 || !nextPaginationStr) {
918
+ logger.info("已到达评论末尾或无更多评论");
919
+ break;
920
+ }
921
+ }
922
+ return {
923
+ ...tmpresp,
924
+ data: {
925
+ ...tmpresp.data,
926
+ replies: Array.from(new Map(fetchedComments.map((item) => [item.rpid, item])).values()).slice(0, Number(data$1.number || 20))
927
+ }
928
+ };
929
+ }
930
+ case "Emoji数据": return await GlobalGetData$3(data$1.methodType, {
931
+ ...baseRequestConfig,
932
+ url: bilibiliApiUrls.表情列表()
933
+ });
934
+ case "番剧基本信息数据": {
935
+ /** 提取出ep_id或season_id */
936
+ let id = data$1.ep_id ? data$1.ep_id : data$1.season_id;
937
+ /** 参数检查 */
938
+ if (!id) return false;
939
+ /** 确定id类型 */
940
+ const idType = id ? id.startsWith("ep") ? "ep_id" : "season_id" : "ep_id";
941
+ const newId = idType === "ep_id" ? id.replace("ep", "") : id.replace("ss", "");
942
+ return await GlobalGetData$3(data$1.methodType, {
943
+ ...baseRequestConfig,
944
+ url: bilibiliApiUrls.番剧明细({ [idType]: newId })
945
+ });
946
+ }
947
+ case "番剧下载信息数据": {
948
+ const SIGN = await qtparam(bilibiliApiUrls.番剧视频流信息({
949
+ cid: data$1.cid,
950
+ ep_id: data$1.ep_id.replace("ep", "")
951
+ }), baseRequestConfig.headers?.cookie);
952
+ return await GlobalGetData$3(data$1.methodType, {
953
+ ...baseRequestConfig,
954
+ url: bilibiliApiUrls.番剧视频流信息({
955
+ cid: data$1.cid,
956
+ ep_id: data$1.ep_id.replace("ep", "")
957
+ }) + SIGN.QUERY
958
+ });
959
+ }
960
+ case "用户主页动态列表数据": {
961
+ const customConfig = {
962
+ ...baseRequestConfig,
963
+ headers: {
964
+ ...baseRequestConfig.headers,
965
+ ...(!requestConfig?.headers || !("referer" in requestConfig.headers)) && { referer: void 0 }
966
+ }
967
+ };
968
+ const { host_mid } = data$1;
969
+ return await GlobalGetData$3(data$1.methodType, {
970
+ ...customConfig,
971
+ url: bilibiliApiUrls.用户空间动态({ host_mid })
972
+ });
973
+ }
974
+ case "动态详情数据": {
975
+ const customConfig = {
976
+ ...baseRequestConfig,
977
+ headers: {
978
+ ...baseRequestConfig.headers,
979
+ ...(!requestConfig?.headers || !("referer" in requestConfig.headers)) && { referer: void 0 }
980
+ }
981
+ };
982
+ return await GlobalGetData$3(data$1.methodType, {
983
+ ...customConfig,
984
+ url: bilibiliApiUrls.动态详情({ dynamic_id: data$1.dynamic_id })
985
+ });
986
+ }
987
+ case "动态卡片数据": {
988
+ const customConfig = {
989
+ ...baseRequestConfig,
990
+ headers: {
991
+ ...baseRequestConfig.headers,
992
+ ...(!requestConfig?.headers || !("referer" in requestConfig.headers)) && { referer: void 0 }
993
+ }
994
+ };
995
+ const { dynamic_id } = data$1;
996
+ return await GlobalGetData$3(data$1.methodType, {
997
+ ...customConfig,
998
+ url: bilibiliApiUrls.动态卡片信息({ dynamic_id })
999
+ });
1000
+ }
1001
+ case "用户主页数据": {
1002
+ const { host_mid } = data$1;
1003
+ return await GlobalGetData$3(data$1.methodType, {
1004
+ ...baseRequestConfig,
1005
+ url: bilibiliApiUrls.用户名片信息({ host_mid })
1006
+ });
1007
+ }
1008
+ case "直播间信息": return await GlobalGetData$3(data$1.methodType, {
1009
+ ...baseRequestConfig,
1010
+ url: bilibiliApiUrls.直播间信息({ room_id: data$1.room_id })
1011
+ });
1012
+ case "直播间初始化信息": return await GlobalGetData$3(data$1.methodType, {
1013
+ ...baseRequestConfig,
1014
+ url: bilibiliApiUrls.直播间初始化信息({ room_id: data$1.room_id })
1015
+ });
1016
+ case "申请二维码": return await GlobalGetData$3(data$1.methodType, {
1017
+ ...baseRequestConfig,
1018
+ url: bilibiliApiUrls.申请二维码()
1019
+ });
1020
+ case "二维码状态": try {
1021
+ const result = await getHeadersAndData({
1022
+ ...baseRequestConfig,
1023
+ url: bilibiliApiUrls.二维码状态({ qrcode_key: data$1.qrcode_key })
1024
+ });
1025
+ if (result.data.code !== 0) {
1026
+ const Err = {
1027
+ errorDescription: `获取响应数据失败!原因:${bilibiliErrorCodeMap[String(result.data.code)] || result.data.message || "未知错误"}!`,
1028
+ requestType: data$1.methodType,
1029
+ requestUrl: bilibiliApiUrls.二维码状态({ qrcode_key: data$1.qrcode_key })
1030
+ };
1031
+ return {
1032
+ code: result.data.code,
1033
+ data: result.data,
1034
+ amagiError: Err
1035
+ };
1036
+ }
1037
+ return {
1038
+ code: 0,
1039
+ data: {
1040
+ data: result.data.data,
1041
+ headers: result.headers
1042
+ },
1043
+ message: "0"
1044
+ };
1045
+ } catch (error) {
1046
+ if (error && typeof error === "object") return error;
1047
+ return {
1048
+ code: amagiAPIErrorCode.UNKNOWN,
1049
+ data: error.data,
1050
+ amagiError: {
1051
+ errorDescription: "未知错误",
1052
+ requestType: data$1.methodType,
1053
+ requestUrl: bilibiliApiUrls.二维码状态({ qrcode_key: data$1.qrcode_key })
1054
+ }
1055
+ };
1056
+ }
1057
+ case "登录基本信息": return await GlobalGetData$3(data$1.methodType, {
1058
+ ...baseRequestConfig,
1059
+ url: bilibiliApiUrls.登录基本信息()
1060
+ });
1061
+ case "获取UP主总播放量": return await GlobalGetData$3(data$1.methodType, {
1062
+ ...baseRequestConfig,
1063
+ url: bilibiliApiUrls.获取UP主总播放量({ host_mid: data$1.host_mid })
1064
+ });
1065
+ case "AV转BV": return {
1066
+ code: 0,
1067
+ message: "success",
1068
+ data: { bvid: av2bv(Number(data$1.avid.toString().replace(/^av/i, ""))) }
1069
+ };
1070
+ case "BV转AV": return {
1071
+ code: 0,
1072
+ message: "success",
1073
+ data: { aid: "av" + bv2av(data$1.bvid) }
1074
+ };
1075
+ case "专栏正文内容": return await GlobalGetData$3(data$1.methodType, {
1076
+ ...baseRequestConfig,
1077
+ url: bilibiliApiUrls.专栏正文内容({ id: data$1.id })
1078
+ });
1079
+ case "专栏显示卡片信息": return await GlobalGetData$3(data$1.methodType, {
1080
+ ...baseRequestConfig,
1081
+ url: bilibiliApiUrls.专栏显示卡片信息({ ids: data$1.ids })
1082
+ });
1083
+ case "专栏文章基本信息": return await GlobalGetData$3(data$1.methodType, {
1084
+ ...baseRequestConfig,
1085
+ url: bilibiliApiUrls.专栏文章基本信息({ id: data$1.id })
1086
+ });
1087
+ case "文集基本信息": return await GlobalGetData$3(data$1.methodType, {
1088
+ ...baseRequestConfig,
1089
+ url: bilibiliApiUrls.文集基本信息({ id: data$1.id })
1090
+ });
1091
+ default:
1092
+ logger.warn(`未知的B站数据接口:「${logger.red(data$1.methodType)}」`);
1093
+ return null;
1094
+ }
1095
+ };
1096
+ /**
1097
+ * 获取数据
1098
+ * @param options - 网络请求配置
1099
+ * @returns
1100
+ */
1101
+ const GlobalGetData$3 = async (type, options) => {
1102
+ let warningMessage = "";
1103
+ try {
1104
+ const result = await fetchData(options);
1105
+ if (!result || result === "") {
1106
+ const Err = {
1107
+ errorDescription: "获取响应数据失败!接口返回内容为空,你的B站ck可能已经失效!",
1108
+ requestType: type ?? "未知请求类型",
1109
+ requestUrl: options.url
1110
+ };
1111
+ warningMessage = `
1112
+ 获取响应数据失败!原因:${logger.yellow("接口返回内容为空,你的B站ck可能已经失效!")}
1113
+ 请求类型:「${type}」
1114
+ 请求URL:${options.url}
1115
+ `;
1116
+ logger.warn(warningMessage);
1117
+ throw {
1118
+ code: bilibiliAPIErrorCode.RISK_CONTROL_FAILED,
1119
+ data: result,
1120
+ amagiError: Err
1121
+ };
1122
+ }
1123
+ if (result.code !== 0 || !result.data || typeof result.data === "object" && Object.keys(result.data).length === 0) {
1124
+ const errorMessage = bilibiliErrorCodeMap[result.code] || typeof result.data === "object" && Object.keys(result.data).length === 0 && "请求成功但无返回内容" || result.message || "未知错误";
1125
+ const Err = {
1126
+ errorDescription: `获取响应数据失败!原因:${errorMessage}!`,
1127
+ requestType: type ?? "未知请求类型",
1128
+ requestUrl: options.url
1129
+ };
1130
+ warningMessage = `
1131
+ 获取响应数据失败!原因:${logger.yellow(errorMessage)}
1132
+ 错误代码:${result.code}
1133
+ 请求类型:「${type}」
1134
+ 请求URL:${options.url}
1135
+ `;
1136
+ logger.warn(warningMessage);
1137
+ throw {
1138
+ code: result.code,
1139
+ data: result,
1140
+ amagiError: Err
1141
+ };
1142
+ }
1143
+ return result;
1144
+ } catch (error) {
1145
+ if (error && typeof error === "object") return {
1146
+ ...error,
1147
+ amagiMessage: warningMessage
1148
+ };
1149
+ return {
1150
+ code: amagiAPIErrorCode.UNKNOWN,
1151
+ data: error.data,
1152
+ amagiError: {
1153
+ errorDescription: "未知错误",
1154
+ requestType: type,
1155
+ requestUrl: options.url
1156
+ },
1157
+ amagiMessage: warningMessage
1158
+ };
1159
+ }
1160
+ };
1161
+ /**
1162
+ * 哔哩哔哩API官方HTTP请求错误码
1163
+ */
1164
+ const bilibiliErrorCodeMap = {
1165
+ "-1": "应用程序不存在或已被封禁",
1166
+ "-2": "Access Key 错误",
1167
+ "-3": "API 校验密匙错误",
1168
+ "-4": "调用方对该 Method 没有权限",
1169
+ "-101": "账号未登录",
1170
+ "-102": "账号被封停",
1171
+ "-103": "积分不足",
1172
+ "-104": "硬币不足",
1173
+ "-105": "验证码错误",
1174
+ "-106": "账号非正式会员或在适应期",
1175
+ "-107": "应用不存在或者被封禁",
1176
+ "-108": "未绑定手机",
1177
+ "-110": "未绑定手机",
1178
+ "-111": "csrf 校验失败",
1179
+ "-112": "系统升级中",
1180
+ "-113": "账号尚未实名认证",
1181
+ "-114": "请先绑定手机",
1182
+ "-115": "请先完成实名认证",
1183
+ "-304": "木有改动",
1184
+ "-307": "撞车跳转",
1185
+ "-352": "风控校验失败 (UA 或 wbi 参数不合法)",
1186
+ "-400": "请求错误",
1187
+ "-401": "未认证 (或非法请求)",
1188
+ "-403": "访问权限不足",
1189
+ "-404": "啥都木有",
1190
+ "-405": "不支持该方法",
1191
+ "-409": "冲突",
1192
+ "-412": "请求被拦截 (客户端 ip 被服务端风控)",
1193
+ "-500": "服务器错误",
1194
+ "-503": "过载保护,服务暂不可用",
1195
+ "-504": "服务调用超时",
1196
+ "-509": "超出限制",
1197
+ "-616": "上传文件不存在",
1198
+ "-617": "上传文件太大",
1199
+ "-625": "登录失败次数太多",
1200
+ "-626": "用户不存在",
1201
+ "-628": "密码太弱",
1202
+ "-629": "用户名或密码错误",
1203
+ "-632": "操作对象数量限制",
1204
+ "-643": "被锁定",
1205
+ "-650": "用户等级太低",
1206
+ "-652": "重复的用户",
1207
+ "-658": "Token 过期",
1208
+ "-662": "密码时间戳过期",
1209
+ "-688": "地理区域限制",
1210
+ "-689": "版权限制",
1211
+ "-701": "扣节操失败",
1212
+ "-799": "请求过于频繁,请稍后再试",
1213
+ "-8888": "对不起,服务器开小差了~ (ಥ﹏ಥ)"
1214
+ };
1215
+
1216
+ //#endregion
1217
+ //#region src/platform/douyin/sign/a_bogus.ts
1218
+ var SM3 = class {
1219
+ reg;
1220
+ chunk;
1221
+ size;
1222
+ constructor() {
1223
+ this.reg = [];
1224
+ this.chunk = [];
1225
+ this.size = 0;
1226
+ this.reset();
1227
+ }
1228
+ reset() {
1229
+ this.reg[0] = 1937774191;
1230
+ this.reg[1] = 1226093241;
1231
+ this.reg[2] = 388252375;
1232
+ this.reg[3] = 3666478592;
1233
+ this.reg[4] = 2842636476;
1234
+ this.reg[5] = 372324522;
1235
+ this.reg[6] = 3817729613;
1236
+ this.reg[7] = 2969243214;
1237
+ this.chunk = [];
1238
+ this.size = 0;
1239
+ }
1240
+ write(e) {
1241
+ const a = typeof e === "string" ? this.stringToBytes(e) : e;
1242
+ this.size += a.length;
1243
+ let f = 64 - this.chunk.length;
1244
+ if (a.length < f) this.chunk = this.chunk.concat(a);
1245
+ else {
1246
+ this.chunk = this.chunk.concat(a.slice(0, f));
1247
+ while (this.chunk.length >= 64) {
1248
+ this._compress(this.chunk);
1249
+ f < a.length ? this.chunk = a.slice(f, Math.min(f + 64, a.length)) : this.chunk = [];
1250
+ f += 64;
1251
+ }
1252
+ }
1253
+ }
1254
+ sum(e, t) {
1255
+ if (e) {
1256
+ this.reset();
1257
+ this.write(e);
1258
+ }
1259
+ this._fill();
1260
+ for (let f = 0; f < this.chunk.length; f += 64) this._compress(this.chunk.slice(f, f + 64));
1261
+ let i = null;
1262
+ if (t === "hex") {
1263
+ i = "";
1264
+ for (let f = 0; f < 8; f++) i += this.padHex(this.reg[f].toString(16), 8);
1265
+ } else {
1266
+ i = new Array(32);
1267
+ for (let f = 0; f < 8; f++) {
1268
+ let c = this.reg[f];
1269
+ i[4 * f + 3] = (255 & c) >>> 0;
1270
+ c >>>= 8;
1271
+ i[4 * f + 2] = (255 & c) >>> 0;
1272
+ c >>>= 8;
1273
+ i[4 * f + 1] = (255 & c) >>> 0;
1274
+ c >>>= 8;
1275
+ i[4 * f] = (255 & c) >>> 0;
1276
+ }
1277
+ }
1278
+ this.reset();
1279
+ return i;
1280
+ }
1281
+ _compress(t) {
1282
+ if (t.length < 64) console.error("compress error: not enough data");
1283
+ else {
1284
+ for (var f = ((e) => {
1285
+ for (var r = new Array(132), t$1 = 0; t$1 < 16; t$1++) r[t$1] = e[4 * t$1] << 24, r[t$1] |= e[4 * t$1 + 1] << 16, r[t$1] |= e[4 * t$1 + 2] << 8, r[t$1] |= e[4 * t$1 + 3], r[t$1] >>>= 0;
1286
+ for (var n = 16; n < 68; n++) {
1287
+ let a = r[n - 16] ^ r[n - 9] ^ this.le(r[n - 3], 15);
1288
+ a = a ^ this.le(a, 15) ^ this.le(a, 23), r[n] = (a ^ this.le(r[n - 13], 7) ^ r[n - 6]) >>> 0;
1289
+ }
1290
+ for (n = 0; n < 64; n++) r[n + 68] = (r[n] ^ r[n + 4]) >>> 0;
1291
+ return r;
1292
+ })(t), i = this.reg.slice(0), c = 0; c < 64; c++) {
1293
+ let o = this.le(i[0], 12) + i[4] + this.le(this.de(c), c);
1294
+ const s = ((o = this.le(o = (4294967295 & o) >>> 0, 7)) ^ this.le(i[0], 12)) >>> 0;
1295
+ let u = this.pe(c, i[0], i[1], i[2]);
1296
+ u = (4294967295 & (u = u + i[3] + s + f[c + 68])) >>> 0;
1297
+ let b = this.he(c, i[4], i[5], i[6]);
1298
+ b = (4294967295 & (b = b + i[7] + o + f[c])) >>> 0, i[3] = i[2], i[2] = this.le(i[1], 9), i[1] = i[0], i[0] = u, i[7] = i[6], i[6] = this.le(i[5], 19), i[5] = i[4], i[4] = (b ^ this.le(b, 9) ^ this.le(b, 17)) >>> 0;
1299
+ }
1300
+ for (let l = 0; l < 8; l++) this.reg[l] = (this.reg[l] ^ i[l]) >>> 0;
1301
+ }
1302
+ }
1303
+ _fill() {
1304
+ let a = 8 * this.size;
1305
+ let f = this.chunk.push(128) % 64;
1306
+ while (64 - f < 8) f -= 64;
1307
+ while (f < 56) {
1308
+ this.chunk.push(0);
1309
+ f++;
1310
+ }
1311
+ for (let i = 0; i < 4; i++) {
1312
+ const c = Math.floor(a / 4294967296);
1313
+ this.chunk.push(c >>> 8 * (3 - i) & 255);
1314
+ }
1315
+ for (let i = 0; i < 4; i++) this.chunk.push(a >>> 8 * (3 - i) & 255);
1316
+ }
1317
+ de(e) {
1318
+ return e >= 0 && e < 16 ? 2043430169 : e >= 16 && e < 64 ? 2055708042 : (console.error("invalid j for constant Tj"), 0);
1319
+ }
1320
+ pe(e, r, t, n) {
1321
+ return e >= 0 && e < 16 ? (r ^ t ^ n) >>> 0 : e >= 16 && e < 64 ? (r & t | r & n | t & n) >>> 0 : (console.error("invalid j for bool function FF"), 0);
1322
+ }
1323
+ he(e, r, t, n) {
1324
+ return e >= 0 && e < 16 ? (r ^ t ^ n) >>> 0 : e >= 16 && e < 64 ? (r & t | ~r & n) >>> 0 : (console.error("invalid j for bool function GG"), 0);
1325
+ }
1326
+ le(e, r) {
1327
+ return (e << (r %= 32) | e >>> 32 - r) >>> 0;
1328
+ }
1329
+ stringToBytes(str) {
1330
+ const n = encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (_, r) => String.fromCharCode(parseInt(r, 16)));
1331
+ const a = new Array(n.length);
1332
+ for (let i = 0; i < n.length; i++) a[i] = n.charCodeAt(i);
1333
+ return a;
1334
+ }
1335
+ padHex(num, size) {
1336
+ return num.padStart(size, "0");
1337
+ }
1338
+ };
1339
+ function rc4_encrypt(plaintext, key) {
1340
+ const s = [];
1341
+ for (var i = 0; i < 256; i++) s[i] = i;
1342
+ var j = 0;
1343
+ for (var i = 0; i < 256; i++) {
1344
+ j = (j + s[i] + key.charCodeAt(i % key.length)) % 256;
1345
+ var temp = s[i];
1346
+ s[i] = s[j];
1347
+ s[j] = temp;
1348
+ }
1349
+ var i = 0;
1350
+ var j = 0;
1351
+ const cipher = [];
1352
+ for (let k = 0; k < plaintext.length; k++) {
1353
+ i = (i + 1) % 256;
1354
+ j = (j + s[i]) % 256;
1355
+ var temp = s[i];
1356
+ s[i] = s[j];
1357
+ s[j] = temp;
1358
+ const t = (s[i] + s[j]) % 256;
1359
+ cipher.push(String.fromCharCode(s[t] ^ plaintext.charCodeAt(k)));
1360
+ }
1361
+ return cipher.join("");
1362
+ }
1363
+ function result_encrypt(long_str, num) {
1364
+ const s_obj = {
1365
+ s0: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
1366
+ s1: "Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe=",
1367
+ s2: "Dkdpgh4ZKsQB80/Mfvw36XI1R25-WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe=",
1368
+ s3: "ckdp1h4ZKsUB80/Mfvw36XIgR25+WQAlEi7NLboqYTOPuzmFjJnryx9HVGDaStCe",
1369
+ s4: "Dkdpgh2ZmsQB80/MfvV36XI1R45-WUAlEixNLwoqYTOPuzKFjJnry79HbGcaStCe"
1370
+ };
1371
+ const constant = {
1372
+ 0: 16515072,
1373
+ 1: 258048,
1374
+ 2: 4032,
1375
+ str: s_obj[num]
1376
+ };
1377
+ let result = "";
1378
+ let lound = 0;
1379
+ let long_int = get_long_int(lound, long_str);
1380
+ for (let i = 0; i < long_str.length / 3 * 4; i++) {
1381
+ if (Math.floor(i / 4) !== lound) {
1382
+ lound += 1;
1383
+ long_int = get_long_int(lound, long_str);
1384
+ }
1385
+ let key = i % 4;
1386
+ let temp_int;
1387
+ switch (key) {
1388
+ case 0:
1389
+ temp_int = (long_int & constant["0"]) >> 18;
1390
+ result += constant["str"].charAt(temp_int);
1391
+ break;
1392
+ case 1:
1393
+ temp_int = (long_int & constant["1"]) >> 12;
1394
+ result += constant["str"].charAt(temp_int);
1395
+ break;
1396
+ case 2:
1397
+ temp_int = (long_int & constant["2"]) >> 6;
1398
+ result += constant["str"].charAt(temp_int);
1399
+ break;
1400
+ case 3:
1401
+ temp_int = long_int & 63;
1402
+ result += constant["str"].charAt(temp_int);
1403
+ break;
1404
+ default: break;
1405
+ }
1406
+ }
1407
+ return result;
1408
+ }
1409
+ function get_long_int(round, long_str) {
1410
+ round = round * 3;
1411
+ return long_str.charCodeAt(round) << 16 | long_str.charCodeAt(round + 1) << 8 | long_str.charCodeAt(round + 2);
1412
+ }
1413
+ function gener_random(random, option) {
1414
+ return [
1415
+ random & 170 | option[0] & 85,
1416
+ random & 85 | option[0] & 170,
1417
+ random >> 8 & 170 | option[1] & 85,
1418
+ random >> 8 & 85 | option[1] & 170
1419
+ ];
1420
+ }
1421
+ function generate_rc4_bb_str(url_search_params, user_agent, window_env_str, suffix = "cus", Arguments = [
1422
+ 0,
1423
+ 1,
1424
+ 14
1425
+ ]) {
1426
+ let sm3 = new SM3();
1427
+ let start_time = Date.now();
1428
+ const url_search_params_list = sm3.sum(sm3.sum(url_search_params + suffix));
1429
+ const cus = sm3.sum(sm3.sum(suffix));
1430
+ const ua = sm3.sum(result_encrypt(rc4_encrypt(user_agent, String.fromCharCode.apply(null, [
1431
+ .00390625,
1432
+ 1,
1433
+ 14
1434
+ ])), "s3"));
1435
+ const end_time = Date.now();
1436
+ let b = {
1437
+ 8: 3,
1438
+ 10: end_time,
1439
+ 15: {
1440
+ aid: 6383,
1441
+ pageId: 6241,
1442
+ boe: false,
1443
+ ddrt: 7,
1444
+ paths: {
1445
+ include: [
1446
+ {},
1447
+ {},
1448
+ {},
1449
+ {},
1450
+ {},
1451
+ {},
1452
+ {}
1453
+ ],
1454
+ exclude: []
1455
+ },
1456
+ track: {
1457
+ mode: 0,
1458
+ delay: 300,
1459
+ paths: []
1460
+ },
1461
+ dump: true,
1462
+ rpU: ""
1463
+ },
1464
+ 16: start_time,
1465
+ 18: 44,
1466
+ 19: [
1467
+ 1,
1468
+ 0,
1469
+ 1,
1470
+ 5
1471
+ ]
1472
+ };
1473
+ b[20] = b[16] >> 24 & 255;
1474
+ b[21] = b[16] >> 16 & 255;
1475
+ b[22] = b[16] >> 8 & 255;
1476
+ b[23] = b[16] & 255;
1477
+ b[24] = b[16] / 256 / 256 / 256 / 256 >> 0;
1478
+ b[25] = b[16] / 256 / 256 / 256 / 256 / 256 >> 0;
1479
+ b[26] = Arguments[0] >> 24 & 255;
1480
+ b[27] = Arguments[0] >> 16 & 255;
1481
+ b[28] = Arguments[0] >> 8 & 255;
1482
+ b[29] = Arguments[0] & 255;
1483
+ b[30] = Arguments[1] / 256 & 255;
1484
+ b[31] = Arguments[1] % 256 & 255;
1485
+ b[32] = Arguments[1] >> 24 & 255;
1486
+ b[33] = Arguments[1] >> 16 & 255;
1487
+ b[34] = Arguments[2] >> 24 & 255;
1488
+ b[35] = Arguments[2] >> 16 & 255;
1489
+ b[36] = Arguments[2] >> 8 & 255;
1490
+ b[37] = Arguments[2] & 255;
1491
+ /** let url_search_params_list = [
1492
+ 91, 186, 35, 86, 143, 253, 6, 76,
1493
+ 34, 21, 167, 148, 7, 42, 192, 219,
1494
+ 188, 20, 182, 85, 213, 74, 213, 147,
1495
+ 37, 155, 93, 139, 85, 118, 228, 213
1496
+ ] */
1497
+ b[38] = url_search_params_list[21];
1498
+ b[39] = url_search_params_list[22];
1499
+ /**
1500
+ * let cus = [
1501
+ 136, 101, 114, 147, 58, 77, 207, 201,
1502
+ 215, 162, 154, 93, 248, 13, 142, 160,
1503
+ 105, 73, 215, 241, 83, 58, 51, 43,
1504
+ 255, 38, 168, 141, 216, 194, 35, 236
1505
+ ] */
1506
+ b[40] = cus[21];
1507
+ b[41] = cus[22];
1508
+ /**
1509
+ * let ua = [
1510
+ 129, 190, 70, 186, 86, 196, 199, 53,
1511
+ 99, 38, 29, 209, 243, 17, 157, 69,
1512
+ 147, 104, 53, 23, 114, 126, 66, 228,
1513
+ 135, 30, 168, 185, 109, 156, 251, 88
1514
+ ] */
1515
+ b[42] = ua[23];
1516
+ b[43] = ua[24];
1517
+ b[44] = b[10] >> 24 & 255;
1518
+ b[45] = b[10] >> 16 & 255;
1519
+ b[46] = b[10] >> 8 & 255;
1520
+ b[47] = b[10] & 255;
1521
+ b[48] = b[8];
1522
+ b[49] = b[10] / 256 / 256 / 256 / 256 >> 0;
1523
+ b[50] = b[10] / 256 / 256 / 256 / 256 / 256 >> 0;
1524
+ b[51] = b[15].pageId;
1525
+ b[52] = b[15].pageId >> 24 & 255;
1526
+ b[53] = b[15].pageId >> 16 & 255;
1527
+ b[54] = b[15].pageId >> 8 & 255;
1528
+ b[55] = b[15].pageId & 255;
1529
+ b[56] = b[15].aid;
1530
+ b[57] = b[15].aid & 255;
1531
+ b[58] = b[15].aid >> 8 & 255;
1532
+ b[59] = b[15].aid >> 16 & 255;
1533
+ b[60] = b[15].aid >> 24 & 255;
1534
+ /**
1535
+ * let window_env_list = [49, 53, 51, 54, 124, 55, 52, 55, 124, 49, 53, 51, 54, 124, 56, 51, 52, 124, 48, 124, 51,
1536
+ * 48, 124, 48, 124, 48, 124, 49, 53, 51, 54, 124, 56, 51, 52, 124, 49, 53, 51, 54, 124, 56,
1537
+ * 54, 52, 124, 49, 53, 50, 53, 124, 55, 52, 55, 124, 50, 52, 124, 50, 52, 124, 87, 105, 110,
1538
+ * 51, 50]
1539
+ */
1540
+ const window_env_list = [];
1541
+ for (let index = 0; index < window_env_str.length; index++) window_env_list.push(window_env_str.charCodeAt(index));
1542
+ b[64] = window_env_list.length;
1543
+ b[65] = b[64] & 255;
1544
+ b[66] = b[64] >> 8 & 255;
1545
+ b[69] = 0;
1546
+ b[70] = b[69] & 255;
1547
+ b[71] = b[69] >> 8 & 255;
1548
+ b[72] = b[18] ^ b[20] ^ b[26] ^ b[30] ^ b[38] ^ b[40] ^ b[42] ^ b[21] ^ b[27] ^ b[31] ^ b[35] ^ b[39] ^ b[41] ^ b[43] ^ b[22] ^ b[28] ^ b[32] ^ b[36] ^ b[23] ^ b[29] ^ b[33] ^ b[37] ^ b[44] ^ b[45] ^ b[46] ^ b[47] ^ b[48] ^ b[49] ^ b[50] ^ b[24] ^ b[25] ^ b[52] ^ b[53] ^ b[54] ^ b[55] ^ b[57] ^ b[58] ^ b[59] ^ b[60] ^ b[65] ^ b[66] ^ b[70] ^ b[71];
1549
+ let bb = [
1550
+ b[18],
1551
+ b[20],
1552
+ b[52],
1553
+ b[26],
1554
+ b[30],
1555
+ b[34],
1556
+ b[58],
1557
+ b[38],
1558
+ b[40],
1559
+ b[53],
1560
+ b[42],
1561
+ b[21],
1562
+ b[27],
1563
+ b[54],
1564
+ b[55],
1565
+ b[31],
1566
+ b[35],
1567
+ b[57],
1568
+ b[39],
1569
+ b[41],
1570
+ b[43],
1571
+ b[22],
1572
+ b[28],
1573
+ b[32],
1574
+ b[60],
1575
+ b[36],
1576
+ b[23],
1577
+ b[29],
1578
+ b[33],
1579
+ b[37],
1580
+ b[44],
1581
+ b[45],
1582
+ b[59],
1583
+ b[46],
1584
+ b[47],
1585
+ b[48],
1586
+ b[49],
1587
+ b[50],
1588
+ b[24],
1589
+ b[25],
1590
+ b[65],
1591
+ b[66],
1592
+ b[70],
1593
+ b[71]
1594
+ ];
1595
+ bb = bb.concat(window_env_list).concat(b[72]);
1596
+ return rc4_encrypt(String.fromCharCode.apply(null, bb), String.fromCharCode.apply(null, [121]));
1597
+ }
1598
+ function generate_random_str() {
1599
+ let random_str_list = [];
1600
+ random_str_list = random_str_list.concat(gener_random(Math.random() * 1e4, [3, 45]));
1601
+ random_str_list = random_str_list.concat(gener_random(Math.random() * 1e4, [1, 0]));
1602
+ random_str_list = random_str_list.concat(gener_random(Math.random() * 1e4, [1, 5]));
1603
+ return String.fromCharCode.apply(null, random_str_list);
1604
+ }
1605
+ /**
1606
+ * 清理User-Agent中的Edge标识
1607
+ * @param userAgent - 原始User-Agent字符串
1608
+ * @returns 清理后的User-Agent字符串
1609
+ */
1610
+ const cleanUserAgentForSigning = (userAgent) => {
1611
+ return userAgent.replace(/\s+Edg\/[\d\.]+/g, "");
1612
+ };
1613
+ /**
1614
+ * 抖音a_bogus签名算法
1615
+ * @param url - 需要签名的URL地址
1616
+ * @param user_agent - 用户代理字符串
1617
+ * @returns 生成的a_bogus签名
1618
+ */
1619
+ var a_bogus_default = (url, user_agent) => {
1620
+ const cleanedUserAgent = cleanUserAgentForSigning(user_agent);
1621
+ return result_encrypt(generate_random_str() + generate_rc4_bb_str(new URLSearchParams(new URL(url).search).toString(), cleanedUserAgent, "1536|747|1536|834|0|30|0|0|1536|834|1536|864|1525|747|24|24|Win32"), "s4") + "=";
1622
+ };
1623
+
1624
+ //#endregion
1625
+ //#region src/platform/douyin/sign/x_bogus.ts
1626
+ /**
1627
+ * X-Bogus 生成工具(TikTok/Douyin 签名算法)- TypeScript 版本
1628
+ * 原始 Python 代码来源:Douyin_TikTok_Download_API (Evil0ctal/Johnserf-Seed)
1629
+ */
1630
+ var XBogus = class {
1631
+ charMap;
1632
+ base64Charset;
1633
+ uaKey;
1634
+ defaultUa;
1635
+ params;
1636
+ xb;
1637
+ constructor() {
1638
+ this.charMap = new Array(128).fill(null);
1639
+ for (let i = 48; i <= 57; i++) this.charMap[i] = i - 48;
1640
+ for (let i = 65; i <= 70; i++) this.charMap[i] = i - 55;
1641
+ for (let i = 97; i <= 102; i++) this.charMap[i] = i - 87;
1642
+ this.base64Charset = "Dkdpgh4ZKsQB80/Mfvw36XI1R25-WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe=";
1643
+ this.uaKey = Buffer.from([
1644
+ 0,
1645
+ 1,
1646
+ 12
1647
+ ]);
1648
+ this.defaultUa = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0";
1649
+ }
1650
+ md5StrToArray(md5Str) {
1651
+ const result = [];
1652
+ if (md5Str.length > 32) {
1653
+ for (const char of md5Str) result.push(char.charCodeAt(0));
1654
+ return result;
1655
+ }
1656
+ let idx = 0;
1657
+ while (idx < md5Str.length) {
1658
+ const leftCharCode = md5Str.charCodeAt(idx);
1659
+ const rightCharCode = md5Str.charCodeAt(idx + 1);
1660
+ const left = this.charMap[leftCharCode];
1661
+ const right = this.charMap[rightCharCode];
1662
+ if (left === null || right === null) throw new Error(`Invalid MD5 character: ${md5Str[idx]}${md5Str[idx + 1]}`);
1663
+ result.push(left << 4 | right);
1664
+ idx += 2;
1665
+ }
1666
+ return result;
1667
+ }
1668
+ md5(input) {
1669
+ const dataArray = typeof input === "string" ? this.md5StrToArray(input) : input;
1670
+ const dataBuffer = Buffer.from(dataArray);
1671
+ return node_crypto.default.createHash("md5").update(dataBuffer).digest("hex");
1672
+ }
1673
+ md5Encrypt(urlPath) {
1674
+ const firstMd5 = this.md5(urlPath);
1675
+ const firstArray = this.md5StrToArray(firstMd5);
1676
+ const secondMd5 = this.md5(firstArray);
1677
+ return this.md5StrToArray(secondMd5);
1678
+ }
1679
+ encodingConversion(...params) {
1680
+ const byteList = [];
1681
+ for (const param of params) if (typeof param === "number") byteList.push(Math.floor(param));
1682
+ else if (typeof param === "string") for (const char of param) byteList.push(char.charCodeAt(0));
1683
+ return Buffer.from(byteList).toString("latin1");
1684
+ }
1685
+ encodingConversion2(a, b, c) {
1686
+ return String.fromCharCode(a) + String.fromCharCode(b) + c;
1687
+ }
1688
+ rc4Encrypt(key, data$1) {
1689
+ const keyBuffer = typeof key === "string" ? Buffer.from(key, "latin1") : key;
1690
+ const dataBuffer = Buffer.from(data$1, "latin1");
1691
+ const S = Array.from({ length: 256 }, (_, i$1) => i$1);
1692
+ let j = 0;
1693
+ for (let i$1 = 0; i$1 < 256; i$1++) {
1694
+ j = (j + S[i$1] + keyBuffer[i$1 % keyBuffer.length]) % 256;
1695
+ [S[i$1], S[j]] = [S[j], S[i$1]];
1696
+ }
1697
+ const encryptedBuffer = Buffer.alloc(dataBuffer.length);
1698
+ let i = 0;
1699
+ j = 0;
1700
+ for (let k = 0; k < dataBuffer.length; k++) {
1701
+ i = (i + 1) % 256;
1702
+ j = (j + S[i]) % 256;
1703
+ [S[i], S[j]] = [S[j], S[i]];
1704
+ const t = (S[i] + S[j]) % 256;
1705
+ encryptedBuffer[k] = dataBuffer[k] ^ S[t];
1706
+ }
1707
+ return encryptedBuffer.toString("latin1");
1708
+ }
1709
+ calculation(a1, a2, a3) {
1710
+ const x3 = (a1 & 255) << 16 | (a2 & 255) << 8 | a3 & 255;
1711
+ const c1 = this.base64Charset[(x3 & 16760832) >> 18];
1712
+ const c2 = this.base64Charset[(x3 & 258048) >> 12];
1713
+ const c3 = this.base64Charset[(x3 & 4032) >> 6];
1714
+ const c4 = this.base64Charset[x3 & 63];
1715
+ return c1 + c2 + c3 + c4;
1716
+ }
1717
+ /**
1718
+ * 生成X-Bogus签名
1719
+ * @param url 完整的URL地址
1720
+ * @param ua 可选的User-Agent,不提供则使用默认值
1721
+ * @returns 包含完整URL、X-Bogus值和使用的User-Agent的元组
1722
+ */
1723
+ getXBogus(url, ua) {
1724
+ const parsedUrl = new node_url.default.URL(url);
1725
+ const urlPath = parsedUrl.pathname + parsedUrl.search;
1726
+ const currentUa = ua || this.defaultUa;
1727
+ const rc4EncryptedUa = this.rc4Encrypt(this.uaKey, currentUa);
1728
+ const base64Ua = Buffer.from(rc4EncryptedUa, "latin1").toString("base64");
1729
+ const md5Ua = this.md5(base64Ua);
1730
+ const array1 = this.md5StrToArray(md5Ua);
1731
+ const array2 = this.md5StrToArray(this.md5(this.md5StrToArray("d41d8cd98f00b204e9800998ecf8427e")));
1732
+ const urlEncryptedArray = this.md5Encrypt(urlPath);
1733
+ const timestamp = Math.floor(Date.now() / 1e3);
1734
+ const ct = 536919696;
1735
+ const newArray = [
1736
+ 64,
1737
+ 1,
1738
+ 1,
1739
+ 12,
1740
+ urlEncryptedArray[14],
1741
+ urlEncryptedArray[15],
1742
+ array2[14],
1743
+ array2[15],
1744
+ array1[14],
1745
+ array1[15],
1746
+ timestamp >> 24 & 255,
1747
+ timestamp >> 16 & 255,
1748
+ timestamp >> 8 & 255,
1749
+ timestamp & 255,
1750
+ ct >> 24 & 255,
1751
+ ct >> 16 & 255,
1752
+ ct >> 8 & 255,
1753
+ ct & 255
1754
+ ];
1755
+ let xorResult = newArray[0];
1756
+ for (let i = 1; i < newArray.length; i++) xorResult ^= newArray[i];
1757
+ newArray.push(xorResult);
1758
+ const array3 = [];
1759
+ const array4 = [];
1760
+ let idx = 0;
1761
+ while (idx < newArray.length) {
1762
+ array3.push(newArray[idx]);
1763
+ if (idx + 1 < newArray.length) array4.push(newArray[idx + 1]);
1764
+ idx += 2;
1765
+ }
1766
+ const mergedArray = [...array3, ...array4];
1767
+ const firstConversion = this.encodingConversion(...mergedArray);
1768
+ const rc4Garbled = this.rc4Encrypt("ÿ", firstConversion);
1769
+ const garbledCode = this.encodingConversion2(2, 255, rc4Garbled);
1770
+ let xb = "";
1771
+ idx = 0;
1772
+ while (idx < garbledCode.length) {
1773
+ if (idx + 2 >= garbledCode.length) break;
1774
+ const a1 = garbledCode.charCodeAt(idx);
1775
+ const a2 = garbledCode.charCodeAt(idx + 1);
1776
+ const a3 = garbledCode.charCodeAt(idx + 2);
1777
+ xb += this.calculation(a1, a2, a3);
1778
+ idx += 3;
1779
+ }
1780
+ return {
1781
+ fullUrl: url.includes("?") ? `${url}&X-Bogus=${xb}` : `${url}?X-Bogus=${xb}`,
1782
+ xbogus: xb,
1783
+ userAgent: currentUa
1784
+ };
1785
+ }
1786
+ };
1787
+
1788
+ //#endregion
1789
+ //#region src/platform/douyin/sign/index.ts
1790
+ const defaultUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36";
1791
+ var douyinSign = class {
1792
+ /**
1793
+ * 生成一个指定长度的随机字符串
1794
+ * @param length 字符串长度,默认为116
1795
+ * @returns
1796
+ */
1797
+ static Mstoken(length) {
1798
+ const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
1799
+ const randomBytes = node_crypto.default.randomBytes(length ?? 116);
1800
+ return Array.from(randomBytes, (byte) => characters[byte % 62]).join("");
1801
+ }
1802
+ /**
1803
+ * a_bogus 签名算法
1804
+ * @param url 需要签名的地址
1805
+ * @returns 对此地址签名后的URL查询参数
1806
+ */
1807
+ static AB(url, userAgent) {
1808
+ return a_bogus_default(url, userAgent || defaultUserAgent);
1809
+ }
1810
+ /**
1811
+ * X-Bogus 签名算法
1812
+ * @param url 需要签名的地址
1813
+ * @returns 对此地址签名后的URL查询参数
1814
+ */
1815
+ static XB(url, userAgent) {
1816
+ return new XBogus().getXBogus(url, userAgent || defaultUserAgent).xbogus;
1817
+ }
1818
+ /** 生成一个唯一的验证字符串 */
1819
+ static VerifyFpManager() {
1820
+ const e = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split("");
1821
+ const t = e.length;
1822
+ const n = (/* @__PURE__ */ new Date()).getTime().toString(36);
1823
+ const r = [];
1824
+ r[8] = "_";
1825
+ r[13] = "_";
1826
+ r[18] = "_";
1827
+ r[23] = "_";
1828
+ r[14] = "4";
1829
+ for (let o, i = 0; i < 36; i++) if (!r[i]) {
1830
+ o = 0 | Math.random() * t;
1831
+ r[i] = e[i === 19 ? 3 & o | 8 : o];
1832
+ }
1833
+ return "verify_" + n + "_" + r.join("");
1834
+ }
1835
+ };
1836
+
1837
+ //#endregion
1838
+ //#region src/platform/douyin/API.ts
1839
+ /**
1840
+ * 从User-Agent中提取浏览器版本信息
1841
+ * @param userAgent - 用户代理字符串
1842
+ * @returns 浏览器版本号,默认为125.0.0.0
1843
+ */
1844
+ const extractBrowserVersion = (userAgent) => {
1845
+ if (!userAgent) return "125.0.0.0";
1846
+ const chromeMatch = userAgent.match(/Chrome\/(\d+\.\d+\.\d+\.\d+)/);
1847
+ if (chromeMatch) return chromeMatch[1];
1848
+ const edgeMatch = userAgent.match(/Edg\/(\d+\.\d+\.\d+\.\d+)/);
1849
+ if (edgeMatch) return edgeMatch[1];
1850
+ return "125.0.0.0";
1851
+ };
1852
+ /**
1853
+ * 将参数对象转换为URL查询字符串
1854
+ * @param params - 参数对象
1855
+ * @returns URL查询字符串
1856
+ */
1857
+ const buildQueryString$1 = (params) => {
1858
+ return Object.entries(params).filter(([_, value]) => value !== void 0 && value !== null).map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join("&");
1859
+ };
1860
+ const fp = douyinSign.VerifyFpManager();
1861
+ var DouyinAPI = class {
1862
+ browserVersion;
1863
+ /**
1864
+ * 构造函数
1865
+ * @param userAgent - 用户代理字符串,用于提取浏览器版本信息
1866
+ */
1867
+ constructor(userAgent) {
1868
+ this.browserVersion = extractBrowserVersion(userAgent);
1869
+ }
1870
+ /**
1871
+ * 获取通用的基础参数
1872
+ * @returns 通用基础参数对象
1873
+ */
1874
+ getBaseParams() {
1875
+ return {
1876
+ device_platform: "webapp",
1877
+ aid: "6383",
1878
+ channel: "channel_pc_web",
1879
+ pc_client_type: "1",
1880
+ cookie_enabled: "true",
1881
+ browser_language: "zh-CN",
1882
+ browser_platform: "Win32",
1883
+ browser_name: "Chrome",
1884
+ browser_version: this.browserVersion,
1885
+ browser_online: "true",
1886
+ engine_name: "Blink",
1887
+ engine_version: this.browserVersion,
1888
+ os_name: "Windows",
1889
+ os_version: "10",
1890
+ cpu_core_num: "16",
1891
+ device_memory: "8",
1892
+ platform: "PC",
1893
+ downlink: "10",
1894
+ effective_type: "4g",
1895
+ msToken: douyinSign.Mstoken(116),
1896
+ verifyFp: fp,
1897
+ fp
1898
+ };
1899
+ }
1900
+ /**
1901
+ * 获取视频或图集数据的接口地址
1902
+ * @param data - 请求参数,包含aweme_id
1903
+ * @returns 完整的接口URL
1904
+ */
1905
+ 视频或图集(data$1) {
1906
+ return `https://www.douyin.com/aweme/v1/web/aweme/detail/?${buildQueryString$1({
1907
+ ...this.getBaseParams(),
1908
+ aweme_id: data$1.aweme_id,
1909
+ update_version_code: "170400",
1910
+ version_code: "190500",
1911
+ version_name: "19.5.0",
1912
+ screen_width: "2328",
1913
+ screen_height: "1310",
1914
+ round_trip_time: "150",
1915
+ webid: "7351848354471872041"
1916
+ })}`;
1917
+ }
1918
+ /**
1919
+ * 获取评论数据的接口地址
1920
+ * @param data - 请求参数,包含aweme_id、cursor、number等
1921
+ * @returns 完整的接口URL
1922
+ */
1923
+ 评论(data$1) {
1924
+ return `https://www.douyin.com/aweme/v1/web/comment/list/?${buildQueryString$1({
1925
+ ...this.getBaseParams(),
1926
+ aweme_id: data$1.aweme_id,
1927
+ cursor: data$1.cursor ?? 0,
1928
+ count: data$1.number ?? 50,
1929
+ item_type: "0",
1930
+ insert_ids: "",
1931
+ whale_cut_token: "",
1932
+ cut_version: "1",
1933
+ rcFT: "",
1934
+ version_code: "170400",
1935
+ version_name: "17.4.0",
1936
+ screen_width: "1552",
1937
+ screen_height: "970",
1938
+ round_trip_time: "50"
1939
+ })}`;
1940
+ }
1941
+ /**
1942
+ * 获取二级评论数据的接口地址
1943
+ * @param data - 请求参数,包含aweme_id、comment_id等
1944
+ * @returns 完整的接口URL
1945
+ */
1946
+ 二级评论(data$1) {
1947
+ return `https://www-hj.douyin.com/aweme/v1/web/comment/list/reply/?${buildQueryString$1({
1948
+ device_platform: "webapp",
1949
+ aid: "6383",
1950
+ channel: "channel_pc_web",
1951
+ item_id: data$1.aweme_id,
1952
+ comment_id: data$1.comment_id,
1953
+ cut_version: "1",
1954
+ cursor: data$1.cursor,
1955
+ count: data$1.number,
1956
+ item_type: "0",
1957
+ update_version_code: "170400",
1958
+ pc_client_type: "1",
1959
+ pc_libra_divert: "Windows",
1960
+ support_h265: "1",
1961
+ support_dash: "1",
1962
+ version_code: "170400",
1963
+ version_name: "17.4.0",
1964
+ cookie_enabled: "true",
1965
+ screen_width: "1552",
1966
+ screen_height: "970",
1967
+ browser_language: "zh-CN",
1968
+ browser_platform: "Win32",
1969
+ browser_name: "Edge",
1970
+ browser_version: this.browserVersion,
1971
+ browser_online: "true",
1972
+ engine_name: "Blink",
1973
+ engine_version: this.browserVersion,
1974
+ os_name: "Windows",
1975
+ os_version: "10",
1976
+ cpu_core_num: "16",
1977
+ device_memory: "8",
1978
+ platform: "PC",
1979
+ downlink: "10",
1980
+ effective_type: "4g",
1981
+ round_trip_time: "50",
1982
+ webid: "7487210762873685515",
1983
+ verifyFp: fp,
1984
+ fp
1985
+ })}`;
1986
+ }
1987
+ /**
1988
+ * 获取动图数据的接口地址
1989
+ * @param data - 请求参数,包含aweme_id
1990
+ * @returns 完整的接口URL
1991
+ */
1992
+ 动图(data$1) {
1993
+ return `https://www.iesdouyin.com/web/api/v2/aweme/slidesinfo/?${buildQueryString$1({
1994
+ reflow_source: "reflow_page",
1995
+ web_id: "7326472315356857893",
1996
+ device_id: "7326472315356857893",
1997
+ aweme_ids: `[${data$1.aweme_id}]`,
1998
+ request_source: "200",
1999
+ msToken: douyinSign.Mstoken(116),
2000
+ verifyFp: fp,
2001
+ fp
2002
+ })}`;
2003
+ }
2004
+ /**
2005
+ * 获取表情数据的接口地址
2006
+ * @returns 完整的接口URL
2007
+ */
2008
+ 表情() {
2009
+ return "https://www.douyin.com/aweme/v1/web/emoji/list";
2010
+ }
2011
+ /**
2012
+ * 获取用户主页视频数据的接口地址
2013
+ * @param data - 请求参数,包含sec_uid
2014
+ * @returns 完整的接口URL
2015
+ */
2016
+ 用户主页视频(data$1) {
2017
+ return `https://www.douyin.com/aweme/v1/web/aweme/post/?${buildQueryString$1({
2018
+ ...this.getBaseParams(),
2019
+ sec_user_id: data$1.sec_uid,
2020
+ max_cursor: "0",
2021
+ locate_query: "false",
2022
+ show_live_replay_strategy: "1",
2023
+ need_time_list: "1",
2024
+ time_list_query: "0",
2025
+ whale_cut_token: "",
2026
+ cut_version: "1",
2027
+ count: "18",
2028
+ publish_video_strategy_type: "2",
2029
+ version_code: "170400",
2030
+ version_name: "17.4.0",
2031
+ screen_width: "1552",
2032
+ screen_height: "970",
2033
+ round_trip_time: "50",
2034
+ webid: "7338423850134226495"
2035
+ })}`;
2036
+ }
2037
+ /**
2038
+ * 获取用户主页信息的接口地址
2039
+ * @param data - 请求参数,包含sec_uid
2040
+ * @returns 完整的接口URL
2041
+ */
2042
+ 用户主页信息(data$1) {
2043
+ return `https://www.douyin.com/aweme/v1/web/user/profile/other/?${buildQueryString$1({
2044
+ ...this.getBaseParams(),
2045
+ publish_video_strategy_type: "2",
2046
+ source: "channel_pc_web",
2047
+ sec_user_id: data$1.sec_uid,
2048
+ personal_center_strategy: "1",
2049
+ version_code: "170400",
2050
+ version_name: "17.4.0",
2051
+ screen_width: "1552",
2052
+ screen_height: "970",
2053
+ round_trip_time: "0",
2054
+ webid: "7327957959955580467"
2055
+ })}`;
2056
+ }
2057
+ /**
2058
+ * 获取热点词数据的接口地址
2059
+ * @param data - 请求参数,包含query
2060
+ * @returns 完整的接口URL
2061
+ */
2062
+ 热点词(data$1) {
2063
+ return `https://www.douyin.com/aweme/v1/web/api/suggest_words/?${buildQueryString$1({
2064
+ ...this.getBaseParams(),
2065
+ query: data$1.query,
2066
+ business_id: "30088",
2067
+ from_group_id: "7129543174929812767",
2068
+ version_code: "170400",
2069
+ version_name: "17.4.0",
2070
+ screen_width: "1552",
2071
+ screen_height: "970",
2072
+ round_trip_time: "50",
2073
+ webid: "7327957959955580467"
2074
+ })}`;
2075
+ }
2076
+ /**
2077
+ * 获取搜索数据的接口地址
2078
+ * @param data - 请求参数,包含query、number、search_id等
2079
+ * @returns 完整的接口URL
2080
+ */
2081
+ 搜索(data$1) {
2082
+ return `https://www.douyin.com/aweme/v1/web/general/search/single/?${buildQueryString$1({
2083
+ ...this.getBaseParams(),
2084
+ search_channel: "aweme_general",
2085
+ sort_type: "0",
2086
+ publish_time: "0",
2087
+ keyword: data$1.query,
2088
+ search_source: "normal_search",
2089
+ query_correct_type: "1",
2090
+ is_filter_search: "0",
2091
+ from_group_id: "",
2092
+ offset: "0",
2093
+ version_code: "190600",
2094
+ version_name: "19.6.0",
2095
+ screen_width: "1552",
2096
+ screen_height: "970",
2097
+ round_trip_time: "50",
2098
+ webid: "7338423850134226495",
2099
+ search_id: data$1.search_id ?? "",
2100
+ count: data$1.number ?? 10
2101
+ })}`;
2102
+ }
2103
+ /**
2104
+ * 获取互动表情数据的接口地址
2105
+ * @returns 完整的接口URL
2106
+ */
2107
+ 互动表情() {
2108
+ return `https://www.douyin.com/aweme/v1/web/im/strategy/config?${buildQueryString$1({
2109
+ device_platform: "webapp",
2110
+ aid: "1128",
2111
+ channel: "channel_pc_web",
2112
+ publish_video_strategy_type: "2",
2113
+ app_id: "1128",
2114
+ scenes: "[%22interactive_resources%22]",
2115
+ pc_client_type: "1",
2116
+ version_code: "170400",
2117
+ version_name: "17.4.0",
2118
+ cookie_enabled: "true",
2119
+ screen_width: "2328",
2120
+ screen_height: "1310",
2121
+ browser_language: "zh-CN",
2122
+ browser_platform: "Win32",
2123
+ browser_name: "Chrome",
2124
+ browser_version: "126.0.0.0",
2125
+ browser_online: "true",
2126
+ engine_name: "Blink",
2127
+ engine_version: "126.0.0.0",
2128
+ os_name: "Windows",
2129
+ os_version: "10",
2130
+ cpu_core_num: "16",
2131
+ device_memory: "8",
2132
+ platform: "PC",
2133
+ downlink: "1.5",
2134
+ effective_type: "4g",
2135
+ round_trip_time: "350",
2136
+ webid: "7347329698282833447",
2137
+ msToken: douyinSign.Mstoken(116),
2138
+ verifyFp: fp,
2139
+ fp
2140
+ })}`;
2141
+ }
2142
+ /**
2143
+ * 获取背景音乐数据的接口地址
2144
+ * @param data - 请求参数,包含music_id
2145
+ * @returns 完整的接口URL
2146
+ */
2147
+ 背景音乐(data$1) {
2148
+ return `https://www.douyin.com/aweme/v1/web/music/detail/?${buildQueryString$1({
2149
+ device_platform: "webapp",
2150
+ aid: "6383",
2151
+ channel: "channel_pc_web",
2152
+ music_id: data$1.music_id,
2153
+ scene: "1",
2154
+ pc_client_type: "1",
2155
+ version_code: "170400",
2156
+ version_name: "17.4.0",
2157
+ cookie_enabled: "true",
2158
+ screen_width: "2328",
2159
+ screen_height: "1310",
2160
+ browser_language: "zh-CN",
2161
+ browser_platform: "Win32",
2162
+ browser_name: "Chrome",
2163
+ browser_version: "126.0.0.0",
2164
+ browser_online: "true",
2165
+ engine_name: "Blink",
2166
+ engine_version: "126.0.0.0",
2167
+ os_name: "Windows",
2168
+ os_version: "10",
2169
+ cpu_core_num: "16",
2170
+ device_memory: "8",
2171
+ platform: "PC",
2172
+ downlink: "1.5",
2173
+ effective_type: "4g",
2174
+ round_trip_time: "350",
2175
+ webid: "7347329698282833447",
2176
+ msToken: douyinSign.Mstoken(116),
2177
+ verifyFp: fp,
2178
+ fp
2179
+ })}`;
2180
+ }
2181
+ /**
2182
+ * 获取直播间信息的接口地址
2183
+ * @param data - 请求参数,包含web_rid、room_id
2184
+ * @returns 完整的接口URL
2185
+ */
2186
+ 直播间信息(data$1) {
2187
+ return `https://live.douyin.com/webcast/room/web/enter/?${buildQueryString$1({
2188
+ aid: "6383",
2189
+ app_name: "douyin_web",
2190
+ live_id: "1",
2191
+ device_platform: "web",
2192
+ language: "zh-CN",
2193
+ enter_from: "web_share_link",
2194
+ cookie_enabled: "true",
2195
+ screen_width: "2048",
2196
+ screen_height: "1152",
2197
+ browser_language: "zh-CN",
2198
+ browser_platform: "Win32",
2199
+ browser_name: "Chrome",
2200
+ browser_version: "125.0.0.0",
2201
+ web_rid: data$1.web_rid,
2202
+ room_id_str: data$1.room_id,
2203
+ enter_source: "",
2204
+ is_need_double_stream: "false",
2205
+ insert_task_id: "",
2206
+ live_reason: "",
2207
+ msToken: douyinSign.Mstoken(116),
2208
+ verifyFp: fp,
2209
+ fp
2210
+ })}`;
2211
+ }
2212
+ /**
2213
+ * 获取申请二维码的接口地址
2214
+ * @param data - 请求参数,包含verify_fp
2215
+ * @returns 完整的接口URL
2216
+ */
2217
+ 申请二维码(data$1) {
2218
+ return `https://sso.douyin.com/get_qrcode/?${buildQueryString$1({
2219
+ verifyFp: data$1.verify_fp,
2220
+ fp: data$1.verify_fp
2221
+ })}`;
2222
+ }
2223
+ /**
2224
+ * 获取弹幕数据的接口地址
2225
+ * @param data - 请求参数,包含group_id、item_id等
2226
+ * @returns 完整的接口URL
2227
+ */
2228
+ 弹幕(data$1) {
2229
+ return `https://www-hj.douyin.com/aweme/v1/web/danmaku/get_v2/?${buildQueryString$1({
2230
+ ...this.getBaseParams(),
2231
+ app_name: "aweme",
2232
+ format: "json",
2233
+ group_id: data$1.aweme_id,
2234
+ item_id: data$1.aweme_id,
2235
+ start_time: data$1.start_time ?? "0",
2236
+ end_time: data$1.end_time ?? "32000",
2237
+ duration: data$1.duration,
2238
+ update_version_code: "170400",
2239
+ pc_libra_divert: "Windows",
2240
+ support_h265: "1",
2241
+ support_dash: "1",
2242
+ version_code: "170400",
2243
+ version_name: "17.4.0",
2244
+ screen_width: "2328",
2245
+ screen_height: "1310",
2246
+ browser_name: "Edge",
2247
+ browser_version: "140.0.0.0",
2248
+ engine_name: "Blink",
2249
+ engine_version: "140.0.0.0",
2250
+ downlink: "1.55",
2251
+ round_trip_time: "200",
2252
+ webid: "7487210762873685515",
2253
+ msToken: douyinSign.Mstoken(116),
2254
+ verifyFp: fp,
2255
+ fp
2256
+ })}`;
2257
+ }
2258
+ };
2259
+ /**
2260
+ * 创建DouyinAPI实例的工厂函数
2261
+ * @param userAgent - 用户代理字符串
2262
+ * @returns DouyinAPI实例
2263
+ */
2264
+ const createDouyinApiUrls = (userAgent) => {
2265
+ return new DouyinAPI(userAgent);
2266
+ };
2267
+ /**
2268
+ * 默认的DouyinAPI实例(使用默认浏览器版本125.0.0.0)
2269
+ * 该类下的所有方法只会返回拼接好参数后的 Url 地址,需要手动请求该地址以获取数据
2270
+ *
2271
+ * 缺少 `a_bougs` 参数,请自行生成拼接
2272
+ */
2273
+ const douyinApiUrls = new DouyinAPI();
2274
+
2275
+ //#endregion
2276
+ //#region src/platform/douyin/getdata.ts
2277
+ /**
2278
+ * 获取签名参数
2279
+ * @param url - 需要签名的URL
2280
+ * @param signType - 签名算法类型
2281
+ * @param userAgent - 用户代理
2282
+ * @returns 签名后的参数字符串
2283
+ */
2284
+ const getSignature = (url, signType = "a_bogus", userAgent) => {
2285
+ switch (signType) {
2286
+ case "x_bogus": return douyinSign.XB(url, userAgent);
2287
+ case "a_bogus":
2288
+ default: return douyinSign.AB(url, userAgent);
2289
+ }
2290
+ };
2291
+ /**
2292
+ * 获取签名参数名称
2293
+ * @param signType - 签名算法类型
2294
+ * @returns 签名参数名称
2295
+ */
2296
+ const getSignParamName = (signType = "a_bogus") => {
2297
+ switch (signType) {
2298
+ case "x_bogus": return "X-Bogus";
2299
+ case "a_bogus":
2300
+ default: return "a_bogus";
2301
+ }
2302
+ };
2303
+ /**
2304
+ * 构建带签名的URL
2305
+ * @param url - 基础URL
2306
+ * @param signType - 签名算法类型
2307
+ * @param userAgent - 用户代理
2308
+ * @returns 带签名的完整URL
2309
+ */
2310
+ const buildSignedUrl = (url, signType = "a_bogus", userAgent) => {
2311
+ const signature = getSignature(url, signType, userAgent);
2312
+ return `${url}&${getSignParamName(signType)}=${signature}`;
2313
+ };
2314
+ /**
2315
+ * 抖音数据获取函数
2316
+ * @param data - 请求数据参数
2317
+ * @param cookie - 用户Cookie
2318
+ * @param requestConfig - 外部请求配置(优先级最高)
2319
+ * @returns 返回抖音数据
2320
+ */
2321
+ const DouyinData = async (data$1, cookie, requestConfig) => {
2322
+ const defHeaders = getDouyinDefaultConfig(cookie)["headers"];
2323
+ const baseRequestConfig = {
2324
+ method: "GET",
2325
+ timeout: 1e4,
2326
+ ...requestConfig,
2327
+ headers: {
2328
+ ...defHeaders,
2329
+ ...requestConfig?.headers || {}
2330
+ }
2331
+ };
2332
+ const userAgent = baseRequestConfig.headers?.["User-Agent"];
2333
+ const douyinApiUrls$1 = createDouyinApiUrls(userAgent);
2334
+ const signType = data$1.signType || "a_bogus";
2335
+ switch (data$1.methodType) {
2336
+ case "文字作品数据":
2337
+ case "聚合解析":
2338
+ case "视频作品数据":
2339
+ case "图集作品数据":
2340
+ case "合辑作品数据": {
2341
+ const url = douyinApiUrls$1.视频或图集({ aweme_id: data$1.aweme_id });
2342
+ return await GlobalGetData$2(data$1.methodType, {
2343
+ ...baseRequestConfig,
2344
+ url: buildSignedUrl(url, signType, userAgent)
2345
+ });
2346
+ }
2347
+ case "评论数据": {
2348
+ const urlGenerator = (params) => douyinApiUrls$1.评论(params);
2349
+ return await fetchPaginatedData(data$1.methodType, urlGenerator, data$1, 50, baseRequestConfig, signType);
2350
+ }
2351
+ case "指定评论回复数据": {
2352
+ const urlGenerator = (params) => douyinApiUrls$1.二级评论(params);
2353
+ return await fetchPaginatedData(data$1.methodType, urlGenerator, data$1, 3, baseRequestConfig, "x_bogus");
2354
+ }
2355
+ case "用户主页数据": {
2356
+ const url = douyinApiUrls$1.用户主页信息({ sec_uid: data$1.sec_uid });
2357
+ const customConfig = {
2358
+ ...baseRequestConfig,
2359
+ headers: {
2360
+ ...baseRequestConfig.headers,
2361
+ ...(!requestConfig?.headers || !("Referer" in requestConfig.headers)) && { Referer: `https://www.douyin.com/user/${data$1.sec_uid}` }
2362
+ }
2363
+ };
2364
+ return await GlobalGetData$2(data$1.methodType, {
2365
+ ...customConfig,
2366
+ url: buildSignedUrl(url, signType, userAgent)
2367
+ });
2368
+ }
2369
+ case "Emoji数据": {
2370
+ const url = douyinApiUrls$1.表情();
2371
+ return await GlobalGetData$2(data$1.methodType, {
2372
+ ...baseRequestConfig,
2373
+ url
2374
+ });
2375
+ }
2376
+ case "用户主页视频列表数据": {
2377
+ const url = douyinApiUrls$1.用户主页视频({ sec_uid: data$1.sec_uid });
2378
+ const customConfig = {
2379
+ ...baseRequestConfig,
2380
+ headers: {
2381
+ ...baseRequestConfig.headers,
2382
+ ...(!requestConfig?.headers || !("Referer" in requestConfig.headers)) && { Referer: `https://www.douyin.com/user/${data$1.sec_uid}` }
2383
+ }
2384
+ };
2385
+ return await GlobalGetData$2(data$1.methodType, {
2386
+ ...customConfig,
2387
+ url: buildSignedUrl(url, signType, userAgent)
2388
+ });
2389
+ }
2390
+ case "热点词数据": {
2391
+ const url = douyinApiUrls$1.热点词({
2392
+ query: data$1.query,
2393
+ number: data$1.number ?? 10
2394
+ });
2395
+ const customConfig = {
2396
+ ...baseRequestConfig,
2397
+ headers: {
2398
+ ...baseRequestConfig.headers,
2399
+ ...(!requestConfig?.headers || !("Referer" in requestConfig.headers)) && { Referer: `https://www.douyin.com/search/${encodeURIComponent(String(data$1.query))}` }
2400
+ }
2401
+ };
2402
+ return await GlobalGetData$2(data$1.methodType, {
2403
+ ...customConfig,
2404
+ url: buildSignedUrl(url, signType, userAgent)
2405
+ });
2406
+ }
2407
+ case "搜索数据": {
2408
+ let search_id = "";
2409
+ const maxPageSize = 15;
2410
+ let fetchedSearchList = [];
2411
+ let tmpresp = {};
2412
+ const customConfig = {
2413
+ ...baseRequestConfig,
2414
+ headers: {
2415
+ ...baseRequestConfig.headers,
2416
+ ...(!requestConfig?.headers || !("Referer" in requestConfig.headers)) && { Referer: `https://www.douyin.com/search/${encodeURIComponent(String(data$1.query))}` }
2417
+ }
2418
+ };
2419
+ while (fetchedSearchList.length < Number(data$1.number ?? 10)) {
2420
+ const requestCount = Math.min(Number(data$1.number ?? 50) - fetchedSearchList.length, maxPageSize);
2421
+ const url = douyinApiUrls$1.搜索({
2422
+ query: data$1.query,
2423
+ number: requestCount,
2424
+ search_id: search_id === "" ? void 0 : search_id
2425
+ });
2426
+ const response = await GlobalGetData$2(data$1.methodType, {
2427
+ ...customConfig,
2428
+ url: buildSignedUrl(url, signType, userAgent)
2429
+ });
2430
+ if (response.data?.length === 0) {
2431
+ logger.warn("获取搜索数据失败!请求成功但接口返回内容为空\n你的抖音ck可能已经失效!\n请求类型:" + data$1.methodType);
2432
+ return false;
2433
+ }
2434
+ if (!response.data) response.data = [];
2435
+ fetchedSearchList.push(...response.data);
2436
+ tmpresp = response;
2437
+ search_id = response.log_pb?.impr_id;
2438
+ }
2439
+ return {
2440
+ ...tmpresp,
2441
+ data: data$1.number === 0 ? [] : fetchedSearchList.slice(0, Number(data$1.number ?? 10))
2442
+ };
2443
+ }
2444
+ case "动态表情数据": {
2445
+ const url = douyinApiUrls$1.互动表情();
2446
+ return await GlobalGetData$2(data$1.methodType, {
2447
+ ...baseRequestConfig,
2448
+ url: buildSignedUrl(url, signType, userAgent)
2449
+ });
2450
+ }
2451
+ case "音乐数据": {
2452
+ const url = douyinApiUrls$1.背景音乐({ music_id: data$1.music_id });
2453
+ return await GlobalGetData$2(data$1.methodType, {
2454
+ ...baseRequestConfig,
2455
+ url: buildSignedUrl(url, signType, userAgent)
2456
+ });
2457
+ }
2458
+ case "直播间信息数据": {
2459
+ let url = douyinApiUrls$1.用户主页信息({ sec_uid: data$1.sec_uid });
2460
+ const fetchUrl = buildSignedUrl(url, signType, userAgent);
2461
+ const customConfig = {
2462
+ ...baseRequestConfig,
2463
+ headers: {
2464
+ ...baseRequestConfig.headers,
2465
+ ...(!requestConfig?.headers || !("Referer" in requestConfig.headers)) && { Referer: `https://www.douyin.com/user/${data$1.sec_uid}` }
2466
+ }
2467
+ };
2468
+ const UserInfoData = await GlobalGetData$2(data$1.methodType, {
2469
+ ...customConfig,
2470
+ url: fetchUrl
2471
+ });
2472
+ if (!UserInfoData?.user?.live_status || UserInfoData.user.live_status !== 1) {
2473
+ logger.error((UserInfoData?.user?.nickname || "用户") + "当前未在直播");
2474
+ const Err = {
2475
+ errorDescription: "检查失败!该用户当前未在直播! TypeError: Cannot read properties of undefined (reading 'live_status')",
2476
+ requestType: data$1.methodType ?? "未知请求类型",
2477
+ requestUrl: fetchUrl
2478
+ };
2479
+ return {
2480
+ code: douoyinAPIErrorCode.NOT_LIVE,
2481
+ data: UserInfoData,
2482
+ amagiError: Err,
2483
+ amagiMessage: Err.errorDescription
2484
+ };
2485
+ }
2486
+ if (!UserInfoData.user.room_data) {
2487
+ logger.error("未获取到直播间信息!");
2488
+ return {
2489
+ code: 500,
2490
+ message: "未获取到直播间信息!",
2491
+ data: null
2492
+ };
2493
+ }
2494
+ const room_data = JSON.parse(UserInfoData.user.room_data);
2495
+ url = douyinApiUrls$1.直播间信息({
2496
+ room_id: UserInfoData.user.room_id_str,
2497
+ web_rid: room_data.owner.web_rid
2498
+ });
2499
+ const liveCustomConfig = {
2500
+ ...baseRequestConfig,
2501
+ headers: {
2502
+ ...baseRequestConfig.headers,
2503
+ ...(!requestConfig?.headers || !("Referer" in requestConfig.headers)) && { Referer: `https://live.douyin.com/${room_data.owner.web_rid}` }
2504
+ }
2505
+ };
2506
+ return await GlobalGetData$2(data$1.methodType, {
2507
+ ...liveCustomConfig,
2508
+ url: buildSignedUrl(url, signType, userAgent)
2509
+ });
2510
+ }
2511
+ case "申请二维码数据": {
2512
+ const url = douyinApiUrls$1.申请二维码({ verify_fp: data$1.verify_fp });
2513
+ return await GlobalGetData$2(data$1.methodType, {
2514
+ ...baseRequestConfig,
2515
+ url: buildSignedUrl(url, signType, userAgent)
2516
+ });
2517
+ }
2518
+ case "弹幕数据": {
2519
+ const MAX_SEGMENT_DURATION = 32e3;
2520
+ const startTime = data$1.start_time ?? 0;
2521
+ const endTime = data$1.end_time ?? data$1.duration;
2522
+ const totalDuration = endTime - startTime;
2523
+ if (totalDuration <= MAX_SEGMENT_DURATION) {
2524
+ const url = douyinApiUrls$1.弹幕({
2525
+ aweme_id: data$1.aweme_id,
2526
+ start_time: startTime,
2527
+ end_time: endTime,
2528
+ duration: data$1.duration
2529
+ });
2530
+ return await GlobalGetData$2(data$1.methodType, {
2531
+ ...baseRequestConfig,
2532
+ url: buildSignedUrl(url, signType, userAgent)
2533
+ });
2534
+ }
2535
+ const segments = [];
2536
+ let currentStart = startTime;
2537
+ while (currentStart < endTime) {
2538
+ const currentEnd = Math.min(currentStart + MAX_SEGMENT_DURATION, endTime);
2539
+ segments.push({
2540
+ start: currentStart,
2541
+ end: currentEnd
2542
+ });
2543
+ currentStart = currentEnd;
2544
+ }
2545
+ logger.debug(`弹幕数据需要分${segments.length}段获取,总时长:${totalDuration}ms`);
2546
+ const segmentPromises = segments.map(async (segment, index) => {
2547
+ const url = douyinApiUrls$1.弹幕({
2548
+ aweme_id: data$1.aweme_id,
2549
+ start_time: segment.start,
2550
+ end_time: segment.end,
2551
+ duration: data$1.duration
2552
+ });
2553
+ try {
2554
+ const segmentData = await GlobalGetData$2(`${data$1.methodType}-段${index + 1}`, {
2555
+ ...baseRequestConfig,
2556
+ url: buildSignedUrl(url, signType, userAgent)
2557
+ });
2558
+ logger.debug(`弹幕第${index + 1}段获取成功 (${segment.start}ms-${segment.end}ms)`);
2559
+ return segmentData;
2560
+ } catch (error) {
2561
+ logger.debug(`弹幕第${index + 1}段获取失败 (${segment.start}ms-${segment.end}ms):`, error);
2562
+ return null;
2563
+ }
2564
+ });
2565
+ const segmentResults = await Promise.all(segmentPromises);
2566
+ const mergedDanmakuList = [];
2567
+ let totalCount = 0;
2568
+ let finalStartTime = startTime;
2569
+ let finalEndTime = endTime;
2570
+ let finalExtra = null;
2571
+ let finalLogPb = null;
2572
+ let finalStatusCode = 0;
2573
+ segmentResults.forEach((segmentData, index) => {
2574
+ if (segmentData && segmentData.danmaku_list) {
2575
+ mergedDanmakuList.push(...segmentData.danmaku_list);
2576
+ totalCount += segmentData.total || 0;
2577
+ if (index === 0) {
2578
+ finalExtra = segmentData.extra;
2579
+ finalLogPb = segmentData.log_pb;
2580
+ finalStatusCode = segmentData.status_code;
2581
+ }
2582
+ }
2583
+ });
2584
+ mergedDanmakuList.sort((a, b) => (a.offset_time || 0) - (b.offset_time || 0));
2585
+ const finalDanmakuData = {
2586
+ danmaku_list: mergedDanmakuList,
2587
+ start_time: finalStartTime,
2588
+ end_time: finalEndTime,
2589
+ total: mergedDanmakuList.length,
2590
+ status_code: finalStatusCode,
2591
+ extra: finalExtra,
2592
+ log_pb: finalLogPb
2593
+ };
2594
+ logger.debug(`弹幕数据合并完成,共获取${mergedDanmakuList.length}条弹幕`);
2595
+ return finalDanmakuData;
2596
+ }
2597
+ default:
2598
+ logger.warn(`未知的抖音数据接口:「${logger.red(data$1.methodType)}」`);
2599
+ return null;
2600
+ }
2601
+ };
2602
+ /**
2603
+ * 通用的分页请求函数
2604
+ * @param type - 请求类型
2605
+ * @param apiUrlGenerator - 接口URL生成器
2606
+ * @param params - 请求参数
2607
+ * @param maxPageSize - 单次请求的最大数据量
2608
+ * @param requestConfig - axios请求配置(外部配置优先)
2609
+ * @param signType - 签名算法类型
2610
+ * @returns 返回分页数据
2611
+ */
2612
+ const fetchPaginatedData = async (type, apiUrlGenerator, params, maxPageSize, requestConfig, signType = "a_bogus") => {
2613
+ let cursor = params.cursor ?? 0;
2614
+ let fetchedData = [];
2615
+ let tmpresp = {};
2616
+ const userAgent = requestConfig.headers?.["User-Agent"];
2617
+ while (fetchedData.length < Number(params.number ?? maxPageSize)) {
2618
+ const requestCount = Math.min(Number(params.number ?? maxPageSize) - fetchedData.length, maxPageSize);
2619
+ const url = apiUrlGenerator({
2620
+ ...params,
2621
+ number: requestCount,
2622
+ cursor
2623
+ });
2624
+ const response = await GlobalGetData$2(type, {
2625
+ ...requestConfig,
2626
+ url: buildSignedUrl(url, signType, userAgent)
2627
+ });
2628
+ fetchedData.push(...response.comments || response.data || []);
2629
+ tmpresp = response;
2630
+ if ((response.comments || response.data || []).length < requestCount) break;
2631
+ cursor = response.cursor;
2632
+ }
2633
+ return {
2634
+ ...tmpresp,
2635
+ comments: params.number === 0 ? [] : fetchedData.slice(0, Number(params.number ?? maxPageSize)),
2636
+ cursor: params.number === 0 ? 0 : fetchedData.length
2637
+ };
2638
+ };
2639
+ /**
2640
+ * 全局数据获取函数
2641
+ * @param type - 请求类型
2642
+ * @param config - 完整的axios请求配置
2643
+ * @returns 返回请求结果或错误详情
2644
+ */
2645
+ const GlobalGetData$2 = async (type, config) => {
2646
+ let warningMessage = "";
2647
+ try {
2648
+ const result = await fetchData(config);
2649
+ if (!result || result === "") {
2650
+ const Err = {
2651
+ errorDescription: "获取响应数据失败!接口返回内容为空,你的抖音ck可能已经失效!",
2652
+ requestType: type ?? "未知请求类型",
2653
+ requestUrl: config.url
2654
+ };
2655
+ warningMessage = `
2656
+ 获取响应数据失败!原因:${logger.yellow("接口返回内容为空,你的抖音ck可能已经失效!")}
2657
+ 请求类型:「${type}」
2658
+ 请求URL:${config.url}
2659
+ `;
2660
+ logger.warn(warningMessage);
2661
+ throw {
2662
+ code: douoyinAPIErrorCode.COOKIE,
2663
+ data: result,
2664
+ amagiError: Err
2665
+ };
2666
+ }
2667
+ if (result.filter_detail && result.filter_detail.filter_reason) {
2668
+ const filterReason = result.filter_detail.filter_reason;
2669
+ const Err = {
2670
+ errorDescription: `获取响应数据失败!原因:${filterReason}!`,
2671
+ requestType: type ?? "未知请求类型",
2672
+ requestUrl: config.url
2673
+ };
2674
+ warningMessage = `
2675
+ 获取响应数据失败!原因:${logger.yellow(filterReason)}
2676
+ 请求类型:「${type}」
2677
+ 请求URL:${config.url}
2678
+ `;
2679
+ logger.warn(warningMessage);
2680
+ throw {
2681
+ code: douoyinAPIErrorCode.FILTER,
2682
+ data: result,
2683
+ amagiError: Err
2684
+ };
2685
+ }
2686
+ return result;
2687
+ } catch (error) {
2688
+ if (error && typeof error === "object") return {
2689
+ ...error,
2690
+ amagiMessage: warningMessage
2691
+ };
2692
+ return {
2693
+ code: amagiAPIErrorCode.UNKNOWN,
2694
+ data: null,
2695
+ amagiError: {
2696
+ errorDescription: "未知错误",
2697
+ requestType: type,
2698
+ requestUrl: config.url
2699
+ },
2700
+ amagiMessage: warningMessage
2701
+ };
2702
+ }
2703
+ };
2704
+
2705
+ //#endregion
2706
+ //#region src/platform/kuaishou/API.ts
2707
+ var API = class {
2708
+ 单个作品信息(data$1) {
2709
+ return {
2710
+ type: "visionVideoDetail",
2711
+ url: "https://www.kuaishou.com/graphql",
2712
+ body: {
2713
+ operationName: "visionVideoDetail",
2714
+ variables: {
2715
+ photoId: data$1.photoId,
2716
+ page: "detail"
2717
+ },
2718
+ query: "query visionVideoDetail($photoId: String, $type: String, $page: String, $webPageArea: String) {\n visionVideoDetail(photoId: $photoId, type: $type, page: $page, webPageArea: $webPageArea) {\n status\n type\n author {\n id\n name\n following\n headerUrl\n __typename\n }\n photo {\n id\n duration\n caption\n likeCount\n realLikeCount\n coverUrl\n photoUrl\n liked\n timestamp\n expTag\n llsid\n viewCount\n videoRatio\n stereoType\n musicBlocked\n manifest {\n mediaType\n businessType\n version\n adaptationSet {\n id\n duration\n representation {\n id\n defaultSelect\n backupUrl\n codecs\n url\n height\n width\n avgBitrate\n maxBitrate\n m3u8Slice\n qualityType\n qualityLabel\n frameRate\n featureP2sp\n hidden\n disableAdaptive\n __typename\n }\n __typename\n }\n __typename\n }\n manifestH265\n photoH265Url\n coronaCropManifest\n coronaCropManifestH265\n croppedPhotoH265Url\n croppedPhotoUrl\n videoResource\n __typename\n }\n tags {\n type\n name\n __typename\n }\n commentLimit {\n canAddComment\n __typename\n }\n llsid\n danmakuSwitch\n __typename\n }\n}\n"
2719
+ }
2720
+ };
2721
+ }
2722
+ 作品评论信息(data$1) {
2723
+ return {
2724
+ type: "commentListQuery",
2725
+ url: "https://www.kuaishou.com/graphql",
2726
+ body: {
2727
+ operationName: "commentListQuery",
2728
+ variables: {
2729
+ photoId: data$1.photoId,
2730
+ pcursor: ""
2731
+ },
2732
+ query: "query commentListQuery($photoId: String, $pcursor: String) {\n visionCommentList(photoId: $photoId, pcursor: $pcursor) {\n commentCount\n pcursor\n rootComments {\n commentId\n authorId\n authorName\n content\n headurl\n timestamp\n likedCount\n realLikedCount\n liked\n status\n authorLiked\n subCommentCount\n subCommentsPcursor\n subComments {\n commentId\n authorId\n authorName\n content\n headurl\n timestamp\n likedCount\n realLikedCount\n liked\n status\n authorLiked\n replyToUserName\n replyTo\n __typename\n }\n __typename\n }\n __typename\n }\n}\n"
2733
+ }
2734
+ };
2735
+ }
2736
+ 表情() {
2737
+ return {
2738
+ type: "visionBaseEmoticons",
2739
+ url: "https://www.kuaishou.com/graphql",
2740
+ body: {
2741
+ operationName: "visionBaseEmoticons",
2742
+ variables: {},
2743
+ query: "query visionBaseEmoticons {\n visionBaseEmoticons {\n iconUrls\n __typename\n }\n}\n"
2744
+ }
2745
+ };
2746
+ }
2747
+ };
2748
+ /** 该类下的所有方法只会返回拼接好参数后的 Url 地址和请求体,需要手动请求该地址以获取数据 */
2749
+ const kuaishouApiUrls = new API();
2750
+
2751
+ //#endregion
2752
+ //#region src/platform/kuaishou/getdata.ts
2753
+ /**
2754
+ * 快手数据获取函数
2755
+ * @param data - 请求数据参数
2756
+ * @param cookie - 用户Cookie
2757
+ * @param requestConfig - 外部请求配置(优先级最高)
2758
+ * @returns 返回快手数据
2759
+ */
2760
+ const KuaishouData = async (data$1, cookie, requestConfig) => {
2761
+ const defHeaders = getKuaishouDefaultConfig(cookie)["headers"];
2762
+ const baseRequestConfig = {
2763
+ method: "POST",
2764
+ timeout: 1e4,
2765
+ ...requestConfig,
2766
+ headers: {
2767
+ ...defHeaders,
2768
+ ...requestConfig?.headers || {}
2769
+ }
2770
+ };
2771
+ switch (data$1.methodType) {
2772
+ case "单个视频作品数据": {
2773
+ const body = kuaishouApiUrls.单个作品信息({ photoId: data$1.photoId });
2774
+ return await GlobalGetData$1(data$1.methodType, {
2775
+ ...baseRequestConfig,
2776
+ url: body.url,
2777
+ data: body.body
2778
+ });
2779
+ }
2780
+ case "评论数据": {
2781
+ const body = kuaishouApiUrls.作品评论信息({ photoId: data$1.photoId });
2782
+ return await GlobalGetData$1(data$1.methodType, {
2783
+ ...baseRequestConfig,
2784
+ url: body.url,
2785
+ data: body.body
2786
+ });
2787
+ }
2788
+ case "Emoji数据": {
2789
+ const body = kuaishouApiUrls.表情();
2790
+ return await GlobalGetData$1(data$1.methodType, {
2791
+ ...baseRequestConfig,
2792
+ url: body.url,
2793
+ data: body.body
2794
+ });
2795
+ }
2796
+ default:
2797
+ logger.warn(`未知的快手数据接口:「${logger.red(data$1.methodType)}」`);
2798
+ return null;
2799
+ }
2800
+ };
2801
+ /**
2802
+ * 数据获取函数
2803
+ * @param options - 网络请求配置选项
2804
+ */
2805
+ const GlobalGetData$1 = async (type, options) => {
2806
+ let warningMessage = "";
2807
+ try {
2808
+ const result = await fetchData(options);
2809
+ if (result === "" || !result || result.result === 2) {
2810
+ const Err = {
2811
+ errorDescription: `获取响应数据失败!接口返回内容为空!`,
2812
+ requestType: type ?? "未知请求类型",
2813
+ requestUrl: options.url,
2814
+ requestBody: JSON.stringify(options.data)
2815
+ };
2816
+ warningMessage = `
2817
+ 获取响应数据失败!原因:${logger.yellow("接口返回内容为空,你的快手ck可能已经失效!")}
2818
+ 请求类型:「${type}」
2819
+ 请求URL:${options.url}
2820
+ 请求参数:${JSON.stringify(options.data, null, 2)}
2821
+ `;
2822
+ logger.warn(warningMessage);
2823
+ throw {
2824
+ code: kuaishouAPIErrorCode.COOKIE,
2825
+ data: result,
2826
+ amagiError: Err
2827
+ };
2828
+ }
2829
+ return result;
2830
+ } catch (error) {
2831
+ if (error && typeof error === "object") return {
2832
+ ...error,
2833
+ amagiMessage: warningMessage
2834
+ };
2835
+ return {
2836
+ code: amagiAPIErrorCode.UNKNOWN,
2837
+ data: null,
2838
+ amagiError: {
2839
+ errorDescription: "未知错误",
2840
+ requestType: type,
2841
+ requestUrl: options.url
2842
+ },
2843
+ amagiMessage: warningMessage
2844
+ };
2845
+ }
2846
+ };
2847
+
2848
+ //#endregion
2849
+ //#region src/validation/utils.ts
2850
+ function smartNumber(errorMessage, minValue = 1, isInteger = false) {
2851
+ if (isInteger) return zod.z.coerce.number({ error: errorMessage }).int({ error: `${errorMessage.replace("不能为空", "")}必须是整数,不能包含小数` }).min(minValue, { error: `${errorMessage.replace("不能为空", "")}必须大于等于${minValue}` });
2852
+ else return zod.z.coerce.number({ error: errorMessage }).min(minValue, { error: `${errorMessage.replace("不能为空", "")}必须大于等于${minValue}` });
2853
+ }
2854
+ /**
2855
+ * 智能正整数转换器 - 专门用于正整数类型的转换
2856
+ * @param errorMessage - 自定义错误信息
2857
+ * @returns Zod正整数验证器
2858
+ */
2859
+ const smartPositiveInteger = (errorMessage) => {
2860
+ return smartNumber(errorMessage, 1, true);
2861
+ };
2862
+ /**
2863
+ * 从页面HTML中提取用户信息
2864
+ * @param html - 包含用户页面HTML的字符串
2865
+ * @returns 提取到的用户信息对象或null
2866
+ */
2867
+ const extractCreatorInfoFromHtml = (html) => {
2868
+ const match = html.match(/<script>window\.__INITIAL_STATE__=(.+)<\/script>/m);
2869
+ if (!match) return null;
2870
+ try {
2871
+ const jsonStr = match[1].replace(/:undefined/g, ":null");
2872
+ return JSON.parse(jsonStr)?.user?.userPageData || null;
2873
+ } catch (error) {
2874
+ console.error("解析用户信息失败:", error);
2875
+ return null;
2876
+ }
2877
+ };
2878
+
2879
+ //#endregion
2880
+ //#region src/validation/douyin.ts
2881
+ const DouyinWorkParamsSchema = zod.z.object({
2882
+ methodType: zod.z.enum([
2883
+ "文字作品数据",
2884
+ "视频作品数据",
2885
+ "图集作品数据",
2886
+ "合辑作品数据",
2887
+ "聚合解析"
2888
+ ], { error: "方法类型必须是指定的枚举值之一" }),
2889
+ aweme_id: zod.z.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" })
2890
+ });
2891
+ const DouyinCommentParamsSchema = zod.z.object({
2892
+ methodType: zod.z.literal("评论数据", { error: "方法类型必须是\"评论数据\"" }),
2893
+ aweme_id: zod.z.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" }),
2894
+ number: smartPositiveInteger("评论数量必须是正整数").optional().default(50),
2895
+ cursor: zod.z.coerce.number({ error: "游标必须是数字" }).int({ error: "游标必须是整数" }).min(0, { error: "游标不能小于0" }).default(0).optional()
2896
+ });
2897
+ const DouyinSearchParamsSchema = zod.z.object({
2898
+ methodType: zod.z.enum(["热点词数据", "搜索数据"], { error: "方法类型必须是\"热点词数据\"或\"搜索数据\"" }),
2899
+ query: zod.z.string({ error: "搜索词必须是字符串" }).min(1, { error: "搜索词不能为空" }),
2900
+ number: smartPositiveInteger("搜索数量必须是正整数").optional().default(10),
2901
+ search_id: zod.z.string({ error: "搜索ID必须是字符串" }).optional()
2902
+ });
2903
+ const DouyinCommentReplyParamsSchema = zod.z.object({
2904
+ methodType: zod.z.literal("指定评论回复数据", { error: "方法类型必须是\"指定评论回复数据\"" }),
2905
+ aweme_id: zod.z.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" }),
2906
+ comment_id: zod.z.string({ error: "评论ID必须是字符串" }).min(1, { error: "评论ID不能为空" }),
2907
+ number: smartPositiveInteger("评论数量必须是正整数").optional().default(5),
2908
+ cursor: zod.z.coerce.number({ error: "游标必须是数字" }).int({ error: "游标必须是整数" }).min(0, { error: "游标不能小于0" }).default(0).optional()
2909
+ });
2910
+ const DouyinUserParamsSchema = zod.z.object({
2911
+ methodType: zod.z.enum([
2912
+ "用户主页数据",
2913
+ "用户主页视频列表数据",
2914
+ "直播间信息数据"
2915
+ ], { error: "方法类型必须是指定的枚举值之一" }),
2916
+ sec_uid: zod.z.string({ error: "用户ID必须是字符串" }).min(1, { error: "用户ID不能为空" })
2917
+ });
2918
+ const DouyinMusicParamsSchema = zod.z.object({
2919
+ methodType: zod.z.literal("音乐数据", { error: "方法类型必须是\"音乐数据\"" }),
2920
+ music_id: zod.z.string({ error: "音乐ID必须是字符串" }).min(1, { error: "音乐ID不能为空" })
2921
+ });
2922
+ const DouyinQrcodeParamsSchema = zod.z.object({
2923
+ methodType: zod.z.literal("申请二维码数据", { error: "方法类型必须是\"申请二维码数据\"" }),
2924
+ verify_fp: zod.z.string({ error: "fp指纹必须是字符串" }).min(1, { error: "fp指纹不能为空" })
2925
+ });
2926
+ const DouyinEmojiListParamsSchema = zod.z.object({ methodType: zod.z.literal("Emoji数据", { error: "方法类型必须是\"Emoji数据\"" }) });
2927
+ const DouyinEmojiProParamsSchema = zod.z.object({ methodType: zod.z.literal("动态表情数据", { error: "方法类型必须是\"动态表情数据\"" }) });
2928
+ const DouyinDanmakuParamsSchema = zod.z.object({
2929
+ methodType: zod.z.literal("弹幕数据", { error: "方法类型必须是\"弹幕数据\"" }),
2930
+ aweme_id: zod.z.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" }),
2931
+ start_time: zod.z.coerce.number({ error: "开始时间必须是数字" }).int({ error: "开始时间必须是整数" }).min(0, { error: "开始时间不能小于0" }).optional(),
2932
+ end_time: zod.z.coerce.number({ error: "结束时间必须是数字" }).int({ error: "结束时间必须是整数" }).min(0, { error: "结束时间不能小于0" }).optional(),
2933
+ duration: zod.z.coerce.number({ error: "视频时长必须是数字" }).int({ error: "视频时长必须是整数" }).min(0, { error: "视频时长不能小于0" })
2934
+ }).refine((data$1) => {
2935
+ if (data$1.end_time !== void 0) return data$1.end_time <= data$1.duration;
2936
+ return true;
2937
+ }, {
2938
+ error: "获取弹幕区间的结束时间不能超过视频总时长",
2939
+ path: ["end_time"]
2940
+ }).refine((data$1) => {
2941
+ if (data$1.start_time !== void 0 && data$1.end_time !== void 0) return data$1.start_time < data$1.end_time;
2942
+ return true;
2943
+ }, {
2944
+ error: "获取弹幕区间的开始时间必须小于结束时间",
2945
+ path: ["start_time"]
2946
+ });
2947
+ const DouyinValidationSchemas = {
2948
+ "文字作品数据": DouyinWorkParamsSchema,
2949
+ "聚合解析": DouyinWorkParamsSchema,
2950
+ "视频作品数据": DouyinWorkParamsSchema,
2951
+ "图集作品数据": DouyinWorkParamsSchema,
2952
+ "合辑作品数据": DouyinWorkParamsSchema,
2953
+ "评论数据": DouyinCommentParamsSchema,
2954
+ "用户主页数据": DouyinUserParamsSchema,
2955
+ "用户主页视频列表数据": DouyinUserParamsSchema,
2956
+ "热点词数据": DouyinSearchParamsSchema,
2957
+ "搜索数据": DouyinSearchParamsSchema,
2958
+ "音乐数据": DouyinMusicParamsSchema,
2959
+ "直播间信息数据": DouyinUserParamsSchema,
2960
+ "申请二维码数据": DouyinQrcodeParamsSchema,
2961
+ "Emoji数据": DouyinEmojiListParamsSchema,
2962
+ "动态表情数据": DouyinEmojiProParamsSchema,
2963
+ "指定评论回复数据": DouyinCommentReplyParamsSchema,
2964
+ "弹幕数据": DouyinDanmakuParamsSchema
2965
+ };
2966
+
2967
+ //#endregion
2968
+ //#region src/validation/bilibili.ts
2969
+ const BilibiliVideoParamsSchema = zod.z.object({
2970
+ methodType: zod.z.literal("单个视频作品数据", { error: "方法类型必须是\"单个视频作品数据\"" }),
2971
+ bvid: zod.z.string({ error: "BVID必须是字符串" }).min(1, { error: "BVID不能为空" })
2972
+ });
2973
+ const BilibiliVideoDownloadParamsSchema = zod.z.object({
2974
+ methodType: zod.z.literal("单个视频下载信息数据", { error: "方法类型必须是\"单个视频下载信息数据\"" }),
2975
+ avid: smartNumber("AVID不能为空", 1, true),
2976
+ cid: smartNumber("CID不能为空", 1, true)
2977
+ });
2978
+ const BilibiliCommentParamsSchema = zod.z.object({
2979
+ methodType: zod.z.literal("评论数据", { error: "方法类型必须是\"评论数据\"" }),
2980
+ oid: zod.z.string({ error: "OID必须是字符串" }).min(1, { error: "OID不能为空" }),
2981
+ type: smartNumber("评论类型不能为空", 1, true).refine((val) => [
2982
+ 1,
2983
+ 2,
2984
+ 4,
2985
+ 5,
2986
+ 6,
2987
+ 7,
2988
+ 8,
2989
+ 9,
2990
+ 10,
2991
+ 11,
2992
+ 12,
2993
+ 13,
2994
+ 14,
2995
+ 15,
2996
+ 16,
2997
+ 17,
2998
+ 18,
2999
+ 19,
3000
+ 20,
3001
+ 21,
3002
+ 22,
3003
+ 33
3004
+ ].includes(val), { error: "无效的评论区类型" }),
3005
+ number: zod.z.coerce.number({ error: "评论数量必须是数字" }).int({ error: "评论数量必须是整数" }).positive({ error: "评论数量必须是正数" }).default(20).optional(),
3006
+ pn: zod.z.coerce.number({ error: "页码必须是数字" }).int({ error: "页码必须是整数" }).positive({ error: "页码必须是正数" }).default(1).optional()
3007
+ });
3008
+ const BilibiliUserParamsSchema = zod.z.object({
3009
+ methodType: zod.z.enum([
3010
+ "用户主页数据",
3011
+ "用户主页动态列表数据",
3012
+ "获取UP主总播放量"
3013
+ ], { error: "方法类型必须是指定的枚举值之一" }),
3014
+ host_mid: smartNumber("UP主UID不能为空", 1, true)
3015
+ });
3016
+ const BilibiliEmojiParamsSchema = zod.z.object({ methodType: zod.z.literal("Emoji数据", { error: "方法类型必须是\"Emoji数据\"" }) });
3017
+ const BilibiliBangumiInfoParamsSchema = zod.z.object({
3018
+ methodType: zod.z.literal("番剧基本信息数据", { error: "方法类型必须是\"番剧基本信息数据\"" }),
3019
+ ep_id: zod.z.string({ error: "番剧EP ID必须是字符串" }).min(1, { error: "番剧EP ID不能为空" }).optional(),
3020
+ season_id: zod.z.string({ error: "番剧季度ID必须是字符串" }).optional()
3021
+ }).refine((data$1) => data$1.ep_id || data$1.season_id, {
3022
+ error: "ep_id 和 season_id 至少需要提供一个",
3023
+ path: ["ep_id"]
3024
+ });
3025
+ const BilibiliBangumiStreamParamsSchema = zod.z.object({
3026
+ methodType: zod.z.literal("番剧下载信息数据", { error: "方法类型必须是\"番剧下载信息数据\"" }),
3027
+ cid: smartNumber("CID不能为空", 1, true),
3028
+ ep_id: zod.z.string({ error: "番剧EP ID必须是字符串" }).min(1, { error: "番剧EP ID不能为空" })
3029
+ });
3030
+ const BilibiliDynamicParamsSchema = zod.z.object({
3031
+ methodType: zod.z.enum(["动态详情数据", "动态卡片数据"], { error: "方法类型必须是\"动态详情数据\"或\"动态卡片数据\"" }),
3032
+ dynamic_id: zod.z.string({ error: "动态ID必须是字符串" }).min(1, { error: "动态ID不能为空" })
3033
+ });
3034
+ const BilibiliLiveParamsSchema = zod.z.object({
3035
+ methodType: zod.z.enum(["直播间信息", "直播间初始化信息"], { error: "方法类型必须是\"直播间信息\"或\"直播间初始化信息\"" }),
3036
+ room_id: zod.z.string({ error: "直播间ID必须是字符串" }).min(1, { error: "直播间ID不能为空" })
3037
+ });
3038
+ const BilibiliLoginParamsSchema = zod.z.object({ methodType: zod.z.literal("登录基本信息", { error: "方法类型必须是\"登录基本信息\"" }) });
3039
+ const BilibiliQrcodeParamsSchema = zod.z.object({ methodType: zod.z.literal("申请二维码", { error: "方法类型必须是\"申请二维码\"" }) });
3040
+ const BilibiliQrcodeStatusParamsSchema = zod.z.object({
3041
+ methodType: zod.z.literal("二维码状态", { error: "方法类型必须是\"二维码状态\"" }),
3042
+ qrcode_key: zod.z.string({ error: "二维码key必须是字符串" }).min(1, { error: "二维码key不能为空" })
3043
+ });
3044
+ const BilibiliAv2BvParamsSchema = zod.z.object({
3045
+ methodType: zod.z.literal("AV转BV", { error: "方法类型必须是\"AV转BV\"" }),
3046
+ avid: zod.z.coerce.number({ error: "AVID必须是数字" }).int({ error: "AVID必须是整数" }).positive({ error: "AVID必须是正数" })
3047
+ });
3048
+ const BilibiliBv2AvParamsSchema = zod.z.object({
3049
+ methodType: zod.z.literal("BV转AV", { error: "方法类型必须是\"BV转AV\"" }),
3050
+ bvid: zod.z.string({ error: "BVID必须是字符串" }).min(1, { error: "BVID不能为空" })
3051
+ });
3052
+ const BilibiliArticleParamsSchema = zod.z.object({
3053
+ methodType: zod.z.literal("专栏正文内容", { error: "方法类型必须是\"专栏正文内容\"" }),
3054
+ id: zod.z.string({ error: "专栏ID必须是字符串" }).min(1, { error: "专栏ID不能为空" })
3055
+ });
3056
+ const BilibiliArticleCardParamsSchema = zod.z.object({
3057
+ methodType: zod.z.literal("专栏显示卡片信息", { error: "方法类型必须是\"专栏显示卡片信息\"" }),
3058
+ ids: zod.z.union([zod.z.array(zod.z.string({ error: "被查询的 id 列表必须是字符串数组" })).min(1, { error: "被查询的 id 列表不能为空" }), zod.z.string({ error: "被查询的 id 列表必须是字符串" }).min(1, { error: "被查询的 id 列表不能为空" })])
3059
+ });
3060
+ const BilibiliArticleInfoParamsSchema = zod.z.object({
3061
+ methodType: zod.z.literal("专栏文章基本信息", { error: "方法类型必须是\"专栏文章基本信息\"" }),
3062
+ id: zod.z.string({ error: "专栏ID必须是字符串" }).min(1, { error: "专栏ID不能为空" })
3063
+ });
3064
+ const BilibiliColumnInfoParamsSchema = zod.z.object({
3065
+ methodType: zod.z.literal("文集基本信息", { error: "方法类型必须是\"文集基本信息\"" }),
3066
+ id: zod.z.string({ error: "文集ID必须是字符串" }).min(1, { error: "文集ID不能为空" })
3067
+ });
3068
+ const BilibiliValidationSchemas = {
3069
+ "单个视频作品数据": BilibiliVideoParamsSchema,
3070
+ "单个视频下载信息数据": BilibiliVideoDownloadParamsSchema,
3071
+ "评论数据": BilibiliCommentParamsSchema,
3072
+ "用户主页数据": BilibiliUserParamsSchema,
3073
+ "用户主页动态列表数据": BilibiliUserParamsSchema,
3074
+ "Emoji数据": BilibiliEmojiParamsSchema,
3075
+ "番剧基本信息数据": BilibiliBangumiInfoParamsSchema,
3076
+ "番剧下载信息数据": BilibiliBangumiStreamParamsSchema,
3077
+ "动态详情数据": BilibiliDynamicParamsSchema,
3078
+ "动态卡片数据": BilibiliDynamicParamsSchema,
3079
+ "直播间信息": BilibiliLiveParamsSchema,
3080
+ "直播间初始化信息": BilibiliLiveParamsSchema,
3081
+ "登录基本信息": BilibiliLoginParamsSchema,
3082
+ "申请二维码": BilibiliQrcodeParamsSchema,
3083
+ "二维码状态": BilibiliQrcodeStatusParamsSchema,
3084
+ "获取UP主总播放量": BilibiliUserParamsSchema,
3085
+ "AV转BV": BilibiliAv2BvParamsSchema,
3086
+ "BV转AV": BilibiliBv2AvParamsSchema,
3087
+ "专栏正文内容": BilibiliArticleParamsSchema,
3088
+ "专栏显示卡片信息": BilibiliArticleCardParamsSchema,
3089
+ "专栏文章基本信息": BilibiliArticleInfoParamsSchema,
3090
+ "文集基本信息": BilibiliColumnInfoParamsSchema
3091
+ };
3092
+
3093
+ //#endregion
3094
+ //#region src/validation/kuaishou.ts
3095
+ const KuaishouVideoParamsSchema = zod.z.object({
3096
+ methodType: zod.z.literal("单个视频作品数据", { error: "方法类型必须是\"单个视频作品数据\"" }),
3097
+ photoId: zod.z.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" })
3098
+ });
3099
+ const KuaishouCommentParamsSchema = zod.z.object({
3100
+ methodType: zod.z.literal("评论数据", { error: "方法类型必须是\"评论数据\"" }),
3101
+ photoId: zod.z.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" })
3102
+ });
3103
+ const KuaishouEmojiParamsSchema = zod.z.object({ methodType: zod.z.literal("Emoji数据", { error: "方法类型必须是\"Emoji数据\"" }) });
3104
+ const KuaishouValidationSchemas = {
3105
+ "单个视频作品数据": KuaishouVideoParamsSchema,
3106
+ "评论数据": KuaishouCommentParamsSchema,
3107
+ "Emoji数据": KuaishouEmojiParamsSchema
3108
+ };
3109
+
3110
+ //#endregion
3111
+ //#region src/platform/xiaohongshu/sign/index.ts
3112
+ /**
3113
+ * 小红书签名算法类
3114
+ */
3115
+ var xiaohongshuSign = class {
3116
+ static client = new __ikenxuan_xhshow_ts.Xhshow();
3117
+ /**
3118
+ * 生成GET请求的X-S签名
3119
+ * @param path - API路径
3120
+ * @param a1Cookie - a1 cookie值
3121
+ * @param clientType - 客户端类型,默认为 'xhs-pc-web'
3122
+ * @param params - 查询参数对象
3123
+ * @returns X-S签名
3124
+ */
3125
+ static generateXSGet(path$1, a1Cookie, clientType = "xhs-pc-web", params = {}) {
3126
+ return this.client.signXsGet(path$1, a1Cookie, clientType, params);
3127
+ }
3128
+ /**
3129
+ * 生成POST请求的X-S签名
3130
+ * @param path - API路径
3131
+ * @param a1Cookie - a1 cookie值
3132
+ * @param clientType - 客户端类型,默认为 'xhs-pc-web'
3133
+ * @param body - 请求体对象
3134
+ * @returns X-S签名
3135
+ */
3136
+ static generateXSPost(path$1, a1Cookie, clientType = "xhs-pc-web", body = {}) {
3137
+ return this.client.signXsPost(path$1, a1Cookie, clientType, body);
3138
+ }
3139
+ /**
3140
+ * 生成X-S签名(兼容旧接口)
3141
+ * @param url - 请求URL
3142
+ * @param body - 请求体
3143
+ * @param userAgent - User-Agent(暂未使用)
3144
+ * @param method - 请求方法,默认为 'POST'
3145
+ * @param a1Cookie - a1 cookie值
3146
+ * @returns X-S签名
3147
+ */
3148
+ static generateXS(url, body, userAgent, method = "POST", a1Cookie = "") {
3149
+ try {
3150
+ const urlObj = new URL(url);
3151
+ const path$1 = urlObj.pathname + urlObj.search;
3152
+ if (method.toUpperCase() === "GET") {
3153
+ const params = typeof body === "object" ? body : {};
3154
+ return this.generateXSGet(path$1, a1Cookie, "xhs-pc-web", params);
3155
+ } else {
3156
+ const requestBody = typeof body === "object" ? body : {};
3157
+ return this.generateXSPost(path$1, a1Cookie, "xhs-pc-web", requestBody);
3158
+ }
3159
+ } catch (error) {
3160
+ console.error("生成X-S签名失败:", error);
3161
+ throw new Error(`签名生成失败: ${error}`);
3162
+ }
3163
+ }
3164
+ /**
3165
+ * 生成X-S-Common参数
3166
+ * @param length - 长度
3167
+ * @returns Base64编码的随机字符串
3168
+ */
3169
+ static generateXSCommon(length = 945) {
3170
+ return node_crypto.default.randomBytes(length).toString("base64").replace(/=+$/, "");
3171
+ }
3172
+ /**
3173
+ * 生成X-T时间戳
3174
+ * @returns 当前时间戳字符串
3175
+ */
3176
+ static generateXT() {
3177
+ return Date.now().toString();
3178
+ }
3179
+ /**
3180
+ * 生成X-B3-Traceid
3181
+ * @returns 16位随机字符串
3182
+ */
3183
+ static generateXB3Traceid() {
3184
+ return Array.from({ length: 16 }, () => "abcdef0123456789"[Math.floor(Math.random() * 16)]).join("");
3185
+ }
3186
+ /**
3187
+ * 从cookie字符串中提取a1值
3188
+ * @param cookieString - 完整的cookie字符串
3189
+ * @returns a1 cookie值
3190
+ */
3191
+ static extractA1FromCookie(cookieString) {
3192
+ const match = cookieString.match(/a1=([^;]+)/);
3193
+ return match ? match[1] : "";
3194
+ }
3195
+ /**
3196
+ * 生成搜索ID
3197
+ * @returns 搜索ID字符串
3198
+ */
3199
+ static getSearchId = () => (BigInt(Date.now()) << 64n) + BigInt(Math.floor(Math.random() * 2147483646)).toString(36);
3200
+ };
3201
+
3202
+ //#endregion
3203
+ //#region src/platform/xiaohongshu/API.ts
3204
+ /**
3205
+ * 搜索排序类型枚举
3206
+ */
3207
+ let SearchSortType = /* @__PURE__ */ function(SearchSortType$1) {
3208
+ /**
3209
+ * 默认排序
3210
+ */
3211
+ SearchSortType$1["GENERAL"] = "general";
3212
+ /**
3213
+ * 最受欢迎(按热度降序)
3214
+ */
3215
+ SearchSortType$1["MOST_POPULAR"] = "popularity_descending";
3216
+ /**
3217
+ * 最新发布(按时间降序)
3218
+ */
3219
+ SearchSortType$1["LATEST"] = "time_descending";
3220
+ return SearchSortType$1;
3221
+ }({});
3222
+ /**
3223
+ * 搜索笔记类型枚举
3224
+ */
3225
+ let SearchNoteType = /* @__PURE__ */ function(SearchNoteType$1) {
3226
+ /**
3227
+ * 默认(全部类型)
3228
+ */
3229
+ SearchNoteType$1[SearchNoteType$1["ALL"] = 0] = "ALL";
3230
+ /**
3231
+ * 仅视频
3232
+ */
3233
+ SearchNoteType$1[SearchNoteType$1["VIDEO"] = 1] = "VIDEO";
3234
+ /**
3235
+ * 仅图片
3236
+ */
3237
+ SearchNoteType$1[SearchNoteType$1["IMAGE"] = 2] = "IMAGE";
3238
+ return SearchNoteType$1;
3239
+ }({});
3240
+ /**
3241
+ * 构建查询字符串
3242
+ * @param params - 参数对象
3243
+ * @returns 查询字符串
3244
+ */
3245
+ const buildQueryString = (params) => {
3246
+ return Object.entries(params).filter(([_, value]) => value !== void 0 && value !== null).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
3247
+ };
3248
+ /**
3249
+ * 小红书API地址配置
3250
+ */
3251
+ const xiaohongshuApiUrls = {
3252
+ 首页推荐数据(data$1 = {}) {
3253
+ return {
3254
+ apiPath: "/api/sns/web/v1/homefeed",
3255
+ Url: "https://edith.xiaohongshu.com/api/sns/web/v1/homefeed",
3256
+ Body: {
3257
+ cursor_score: data$1.cursor_score || "1.7599348899670024E9",
3258
+ num: data$1.num || 33,
3259
+ refresh_type: data$1.refresh_type || 3,
3260
+ note_index: data$1.note_index || 33,
3261
+ category: data$1.category || "homefeed_recommend",
3262
+ search_key: data$1.search_key || "",
3263
+ image_formats: [
3264
+ "jpg",
3265
+ "webp",
3266
+ "avif"
3267
+ ]
3268
+ }
3269
+ };
3270
+ },
3271
+ 单个笔记数据(data$1) {
3272
+ return {
3273
+ apiPath: "/api/sns/web/v1/feed",
3274
+ Url: "https://edith.xiaohongshu.com/api/sns/web/v1/feed",
3275
+ Body: {
3276
+ source_note_id: data$1.note_id,
3277
+ image_formats: [
3278
+ "jpg",
3279
+ "webp",
3280
+ "avif"
3281
+ ],
3282
+ extra: { need_body_topic: "1" },
3283
+ xsec_source: "pc_feed",
3284
+ xsec_token: data$1.xsec_token
3285
+ }
3286
+ };
3287
+ },
3288
+ 评论数据(data$1) {
3289
+ return {
3290
+ apiPath: "/api/sns/web/v2/comment/page",
3291
+ Url: `https://edith.xiaohongshu.com/api/sns/web/v2/comment/page?${buildQueryString({
3292
+ note_id: data$1.note_id,
3293
+ cursor: data$1.cursor || "",
3294
+ image_formats: [
3295
+ "jpg",
3296
+ "webp",
3297
+ "avif"
3298
+ ].join(","),
3299
+ xsec_token: data$1.xsec_token
3300
+ })}`
3301
+ };
3302
+ },
3303
+ 用户数据(data$1) {
3304
+ return {
3305
+ apiPath: "/api/sns/web/v1/user/otherinfo",
3306
+ Url: `https://www.xiaohongshu.com/user/profile/${data$1.user_id}`
3307
+ };
3308
+ },
3309
+ 用户笔记数据(data$1) {
3310
+ return {
3311
+ apiPath: "/api/sns/web/v1/user_posted",
3312
+ Url: `https://edith.xiaohongshu.com/api/sns/web/v1/user_posted?${buildQueryString({
3313
+ user_id: data$1.user_id,
3314
+ cursor: data$1.cursor || "",
3315
+ num: data$1.num || 30,
3316
+ image_formats: [
3317
+ "jpg",
3318
+ "webp",
3319
+ "avif"
3320
+ ].join(","),
3321
+ xsec_source: "pc_feed"
3322
+ })}`
3323
+ };
3324
+ },
3325
+ 表情列表(data$1) {
3326
+ return {
3327
+ apiPath: "/api/im/redmoji/detail",
3328
+ Url: "https://edith.xiaohongshu.com/api/im/redmoji/detail"
3329
+ };
3330
+ },
3331
+ 搜索笔记(data$1) {
3332
+ return {
3333
+ apiPath: "/api/sns/web/v1/search/notes",
3334
+ Body: {
3335
+ keyword: data$1.keyword,
3336
+ page: data$1.page || 1,
3337
+ page_size: data$1.page_size || 20,
3338
+ sort: SearchSortType.GENERAL,
3339
+ note_type: SearchNoteType.ALL,
3340
+ search_id: xiaohongshuSign.getSearchId(),
3341
+ image_formats: [
3342
+ "jpg",
3343
+ "webp",
3344
+ "avif"
3345
+ ]
3346
+ },
3347
+ Url: "https://edith.xiaohongshu.com/api/sns/web/v1/search/notes"
3348
+ };
3349
+ }
3350
+ };
3351
+ /**
3352
+ * 创建小红书API URLs实例
3353
+ * @returns 小红书API URLs对象
3354
+ */
3355
+ const createXiaohongshuApiUrls = () => {
3356
+ return xiaohongshuApiUrls;
3357
+ };
3358
+
3359
+ //#endregion
3360
+ //#region src/validation/xiaohongshu.ts
3361
+ const SearchSortTypeValues = Object.values(SearchSortType).filter((v) => typeof v === "string");
3362
+ const SearchNoteTypeValues = Object.values(SearchNoteType).filter((v) => typeof v === "number");
3363
+ /**
3364
+ * 小红书首页推荐数据参数验证模式
3365
+ */
3366
+ const HomeFeedParamsSchema = zod.z.object({
3367
+ methodType: zod.z.literal("首页推荐数据", { error: "方法类型必须是\"首页推荐数据\"" }),
3368
+ cursor_score: zod.z.string({ error: "cursor_score必须是字符串" }).optional(),
3369
+ num: zod.z.coerce.number({ error: "数量必须是数字" }).int({ error: "数量必须是整数" }).min(1, { error: "数量不能小于1" }).max(100, { error: "数量不能大于100" }).optional(),
3370
+ refresh_type: zod.z.coerce.number({ error: "refresh_type必须是数字" }).int({ error: "refresh_type必须是整数" }).optional(),
3371
+ note_index: zod.z.coerce.number({ error: "note_index必须是数字" }).int({ error: "note_index必须是整数" }).optional(),
3372
+ category: zod.z.string({ error: "category必须是字符串" }).optional(),
3373
+ search_key: zod.z.string({ error: "search_key必须是字符串" }).optional()
3374
+ });
3375
+ /**
3376
+ * 小红书单个笔记数据参数验证模式
3377
+ */
3378
+ const NoteParamsSchema = zod.z.object({
3379
+ methodType: zod.z.literal("单个笔记数据", { error: "方法类型必须是\"单个笔记数据\"" }),
3380
+ note_id: zod.z.string({ error: "note_id必须是字符串" }),
3381
+ xsec_token: zod.z.string({ error: "xsec_token必须是字符串" })
3382
+ });
3383
+ /**
3384
+ * 小红书评论数据参数验证模式
3385
+ */
3386
+ const CommentParamsSchema = zod.z.object({
3387
+ methodType: zod.z.literal("评论数据", { error: "方法类型必须是\"评论数据\"" }),
3388
+ note_id: zod.z.string({ error: "note_id必须是字符串" }),
3389
+ cursor: zod.z.string({ error: "cursor必须是字符串" }).optional(),
3390
+ xsec_token: zod.z.string({ error: "xsec_token必须是字符串" })
3391
+ });
3392
+ /**
3393
+ * 小红书用户数据参数验证模式
3394
+ */
3395
+ const UserParamsSchema = zod.z.object({
3396
+ methodType: zod.z.literal("用户数据", { error: "方法类型必须是\"用户数据\"" }),
3397
+ user_id: zod.z.string({ error: "user_id必须是字符串" })
3398
+ });
3399
+ /**
3400
+ * 小红书用户笔记数据参数验证模式
3401
+ */
3402
+ const UserNoteParamsSchema = zod.z.object({
3403
+ methodType: zod.z.literal("用户笔记数据", { error: "方法类型必须是\"用户笔记数据\"" }),
3404
+ user_id: zod.z.string({ error: "user_id必须是字符串" }),
3405
+ cursor: zod.z.string({ error: "cursor必须是字符串" }).optional(),
3406
+ num: zod.z.coerce.number({ error: "数量必须是数字" }).int({ error: "数量必须是整数" }).min(1, { error: "数量不能小于1" }).max(100, { error: "数量不能大于100" }).optional()
3407
+ });
3408
+ const EmojiListParamsSchema = zod.z.object({ methodType: zod.z.literal("表情列表", { error: "方法类型必须是\"表情列表\"" }) });
3409
+ /**
3410
+ * 小红书搜索笔记参数验证模式
3411
+ */
3412
+ const SearchNoteParamsSchema = zod.z.object({
3413
+ methodType: zod.z.literal("搜索笔记", { error: "方法类型必须是\"搜索笔记\"" }),
3414
+ keyword: zod.z.string({ error: "keyword必须是字符串" }),
3415
+ page: zod.z.coerce.number({ error: "page必须是数字" }).int({ error: "page必须是整数" }).min(1, { error: "page不能小于1" }).optional(),
3416
+ page_size: zod.z.coerce.number({ error: "page_size必须是数字" }).int({ error: "page_size必须是整数" }).min(1, { error: "page_size不能小于1" }).max(100, { error: "page_size不能大于100" }).optional(),
3417
+ sort: zod.z.enum(SearchSortTypeValues, { error: "排序类型不合法" }).optional(),
3418
+ note_type: zod.z.coerce.number({ error: "笔记类型必须是数字" }).int({ error: "笔记类型必须是整数" }).refine((val) => SearchNoteTypeValues.includes(val), { message: "笔记类型不合法" }).optional()
3419
+ });
3420
+ /**
3421
+ * 小红书验证模式映射
3422
+ */
3423
+ const XiaohongshuValidationSchemas = {
3424
+ 首页推荐数据: HomeFeedParamsSchema,
3425
+ 单个笔记数据: NoteParamsSchema,
3426
+ 评论数据: CommentParamsSchema,
3427
+ 用户数据: UserParamsSchema,
3428
+ 用户笔记数据: UserNoteParamsSchema,
3429
+ 表情列表: EmojiListParamsSchema,
3430
+ 搜索笔记: SearchNoteParamsSchema
3431
+ };
3432
+ /**
3433
+ * 验证小红书参数
3434
+ * @param methodType - 小红书方法类型
3435
+ * @param params - 待验证的参数
3436
+ * @returns 验证后的参数
3437
+ */
3438
+ const validateXiaohongshuParams = (methodType, params) => {
3439
+ return XiaohongshuValidationSchemas[methodType].parse(typeof params === "object" && params !== null ? {
3440
+ methodType,
3441
+ ...params
3442
+ } : {
3443
+ methodType,
3444
+ params
3445
+ });
3446
+ };
3447
+
3448
+ //#endregion
3449
+ //#region src/validation/index.ts
3450
+ /**
3451
+ * 验证抖音参数
3452
+ * @param methodType - 抖音方法类型
3453
+ * @param params - 待验证的参数
3454
+ * @returns 验证后的参数,符合原始API期望的类型
3455
+ */
3456
+ const validateDouyinParams = (methodType, params) => {
3457
+ return DouyinValidationSchemas[methodType].parse(typeof params === "object" && params !== null ? {
3458
+ methodType,
3459
+ ...params
3460
+ } : {
3461
+ methodType,
3462
+ params
3463
+ });
3464
+ };
3465
+ /**
3466
+ * 验证哔哩哔哩参数
3467
+ * @param methodType - 哔哩哔哩方法类型
3468
+ * @param params - 待验证的参数
3469
+ * @returns 验证后的参数,符合原始API期望的类型
3470
+ */
3471
+ const validateBilibiliParams = (methodType, params) => {
3472
+ return BilibiliValidationSchemas[methodType].parse(typeof params === "object" && params !== null ? {
3473
+ methodType,
3474
+ ...params
3475
+ } : {
3476
+ methodType,
3477
+ params
3478
+ });
3479
+ };
3480
+ /**
3481
+ * 验证快手参数
3482
+ * @param methodType - 快手方法类型
3483
+ * @param params - 待验证的参数
3484
+ * @returns 验证后的参数,符合原始API期望的类型
3485
+ */
3486
+ const validateKuaishouParams = (methodType, params) => {
3487
+ return KuaishouValidationSchemas[methodType].parse(typeof params === "object" && params !== null ? {
3488
+ methodType,
3489
+ ...params
3490
+ } : {
3491
+ methodType,
3492
+ params
3493
+ });
3494
+ };
3495
+ /**
3496
+ * 创建成功响应格式
3497
+ * @param data - 响应数据
3498
+ * @param message - 响应消息(可选)
3499
+ * @param code - 响应状态码(可选,默认200)
3500
+ * @returns 格式化的成功API响应对象
3501
+ */
3502
+ const createSuccessResponse = (data$1, message, code = 200) => {
3503
+ return {
3504
+ success: true,
3505
+ data: data$1,
3506
+ message,
3507
+ code,
3508
+ error: void 0
3509
+ };
3510
+ };
3511
+ /**
3512
+ * 创建失败响应格式
3513
+ * @param error - 错误信息
3514
+ * @param message - 详细错误消息(可选)
3515
+ * @param code - 错误状态码(可选,默认500)
3516
+ * @returns 格式化的错误响应对象
3517
+ */
3518
+ const createErrorResponse = (error, message, code = 500) => {
3519
+ return {
3520
+ success: false,
3521
+ error,
3522
+ message,
3523
+ code,
3524
+ data: void 0
3525
+ };
3526
+ };
3527
+
3528
+ //#endregion
3529
+ //#region src/platform/xiaohongshu/getdata.ts
3530
+ /**
3531
+ * 小红书数据获取函数
3532
+ * @param data - 请求数据参数
3533
+ * @param cookie - 用户Cookie
3534
+ * @param requestConfig - 外部请求配置
3535
+ * @returns 返回小红书数据
3536
+ */
3537
+ const XiaohongshuData = async (data$1, cookie, requestConfig) => {
3538
+ const defHeaders = getXiaohongshuDefaultConfig(cookie)["headers"];
3539
+ const baseRequestConfig = {
3540
+ method: "POST",
3541
+ timeout: 1e4,
3542
+ ...requestConfig,
3543
+ headers: {
3544
+ ...defHeaders,
3545
+ ...requestConfig?.headers || {}
3546
+ }
3547
+ };
3548
+ const xiaohongshuApiUrls$1 = createXiaohongshuApiUrls();
3549
+ switch (data$1.methodType) {
3550
+ case "首页推荐数据": return await GlobalGetData(data$1.methodType, {
3551
+ ...baseRequestConfig,
3552
+ url: xiaohongshuApiUrls$1.首页推荐数据(data$1).Url,
3553
+ data: JSON.stringify(xiaohongshuApiUrls$1.首页推荐数据(data$1).Body),
3554
+ headers: {
3555
+ ...baseRequestConfig.headers,
3556
+ "x-s": xiaohongshuSign.generateXSPost(xiaohongshuApiUrls$1.首页推荐数据(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie || ""), "xhs-pc-web", xiaohongshuApiUrls$1.首页推荐数据(data$1).Body),
3557
+ "x-s-common": xiaohongshuSign.generateXSCommon(),
3558
+ "x-t": xiaohongshuSign.generateXT()
3559
+ }
3560
+ });
3561
+ case "单个笔记数据": return await GlobalGetData(data$1.methodType, {
3562
+ ...baseRequestConfig,
3563
+ url: xiaohongshuApiUrls$1.单个笔记数据(data$1).Url,
3564
+ data: xiaohongshuApiUrls$1.单个笔记数据(data$1).Body,
3565
+ headers: {
3566
+ ...baseRequestConfig.headers,
3567
+ "x-s": xiaohongshuSign.generateXSPost(xiaohongshuApiUrls$1.单个笔记数据(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie || ""), "xhs-pc-web", xiaohongshuApiUrls$1.单个笔记数据(data$1).Body),
3568
+ "x-s-common": xiaohongshuSign.generateXSCommon(),
3569
+ "x-t": xiaohongshuSign.generateXT()
3570
+ }
3571
+ });
3572
+ case "评论数据": {
3573
+ const baseRequestConfig$1 = {
3574
+ method: "GET",
3575
+ timeout: 1e4,
3576
+ ...requestConfig,
3577
+ headers: {
3578
+ ...defHeaders,
3579
+ ...requestConfig?.headers || {}
3580
+ }
3581
+ };
3582
+ return await GlobalGetData(data$1.methodType, {
3583
+ ...baseRequestConfig$1,
3584
+ url: xiaohongshuApiUrls$1.评论数据(data$1).Url,
3585
+ headers: {
3586
+ ...baseRequestConfig$1.headers,
3587
+ "x-s": xiaohongshuSign.generateXSGet(xiaohongshuApiUrls$1.评论数据(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie || ""), "xhs-pc-web"),
3588
+ "x-s-common": xiaohongshuSign.generateXSCommon(),
3589
+ "x-t": xiaohongshuSign.generateXT()
3590
+ }
3591
+ });
3592
+ }
3593
+ case "用户数据": {
3594
+ const baseRequestConfig$1 = {
3595
+ method: "GET",
3596
+ timeout: 1e4,
3597
+ ...requestConfig,
3598
+ headers: {
3599
+ ...defHeaders,
3600
+ ...requestConfig?.headers || {}
3601
+ }
3602
+ };
3603
+ return {
3604
+ code: 0,
3605
+ data: extractCreatorInfoFromHtml(await GlobalGetData(data$1.methodType, {
3606
+ ...baseRequestConfig$1,
3607
+ url: xiaohongshuApiUrls$1.用户数据(data$1).Url,
3608
+ headers: {
3609
+ ...baseRequestConfig$1.headers,
3610
+ "x-s": xiaohongshuSign.generateXSGet(xiaohongshuApiUrls$1.用户数据(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie || ""), "xhs-pc-web"),
3611
+ "x-s-common": xiaohongshuSign.generateXSCommon(),
3612
+ "x-t": xiaohongshuSign.generateXT()
3613
+ }
3614
+ })),
3615
+ msg: "成功"
3616
+ };
3617
+ }
3618
+ case "用户笔记数据": return await GlobalGetData(data$1.methodType, {
3619
+ ...baseRequestConfig,
3620
+ method: "GET",
3621
+ url: xiaohongshuApiUrls$1.用户笔记数据(data$1).Url,
3622
+ headers: {
3623
+ ...baseRequestConfig.headers,
3624
+ "x-b3-traceid": xiaohongshuSign.generateXB3Traceid(),
3625
+ "x-s": xiaohongshuSign.generateXSGet(xiaohongshuApiUrls$1.用户笔记数据(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie || ""), "xhs-pc-web"),
3626
+ "x-s-common": xiaohongshuSign.generateXSCommon(),
3627
+ "x-t": xiaohongshuSign.generateXT()
3628
+ }
3629
+ });
3630
+ case "表情列表": {
3631
+ const baseRequestConfig$1 = {
3632
+ method: "GET",
3633
+ timeout: 1e4,
3634
+ ...requestConfig,
3635
+ headers: {
3636
+ ...defHeaders,
3637
+ ...requestConfig?.headers || {}
3638
+ }
3639
+ };
3640
+ return await GlobalGetData(data$1.methodType, {
3641
+ ...baseRequestConfig$1,
3642
+ url: xiaohongshuApiUrls$1.表情列表(data$1).Url,
3643
+ headers: {
3644
+ ...baseRequestConfig$1.headers,
3645
+ "x-s": xiaohongshuSign.generateXSGet(xiaohongshuApiUrls$1.表情列表(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie || ""), "xhs-pc-web"),
3646
+ "x-s-common": xiaohongshuSign.generateXSCommon(),
3647
+ "x-t": xiaohongshuSign.generateXT()
3648
+ }
3649
+ });
3650
+ }
3651
+ case "搜索笔记": return await GlobalGetData(data$1.methodType, {
3652
+ ...baseRequestConfig,
3653
+ url: xiaohongshuApiUrls$1.搜索笔记(data$1).Url,
3654
+ data: xiaohongshuApiUrls$1.搜索笔记(data$1).Body,
3655
+ headers: {
3656
+ ...baseRequestConfig.headers,
3657
+ "x-s": xiaohongshuSign.generateXSPost(xiaohongshuApiUrls$1.搜索笔记(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie || ""), "xhs-pc-web"),
3658
+ "x-s-common": xiaohongshuSign.generateXSCommon(),
3659
+ "x-t": xiaohongshuSign.generateXT()
3660
+ }
3661
+ });
3662
+ default: throw new Error(`未知的小红书数据接口: 「${logger.red(data$1.methodType)}」`);
3663
+ }
3664
+ };
3665
+ /**
3666
+ * 全局数据获取函数
3667
+ */
3668
+ const GlobalGetData = async (methodType, config) => {
3669
+ try {
3670
+ const response = await fetchData(config);
3671
+ if (typeof response === "string" && response.includes("<html>")) return response;
3672
+ if (response.code !== 0) throw new Error(`API请求失败: ${response.data?.msg || response.msg || "未知错误"}, code: ${response.code}`);
3673
+ return response;
3674
+ } catch (error) {
3675
+ logger.error(`小红书API请求失败 [${methodType}]:`, error.message);
3676
+ return {
3677
+ code: 500,
3678
+ message: "error",
3679
+ data: null,
3680
+ amagiError: {
3681
+ errorDescription: error.message || "未知错误",
3682
+ requestType: methodType,
3683
+ requestUrl: config.url || ""
3684
+ },
3685
+ amagiMessage: `小红书API请求失败: ${error.message}`
3686
+ };
3687
+ }
3688
+ };
3689
+
3690
+ //#endregion
3691
+ //#region src/model/DataFetchers.ts
3692
+ /**
3693
+ * 获取抖音数据的核心方法实现
3694
+ */
3695
+ async function getDouyinData(methodType, optionsOrCookie, cookieOrOptions, requestConfig) {
3696
+ try {
3697
+ let options;
3698
+ let cookie;
3699
+ let config;
3700
+ if (typeof optionsOrCookie === "string") {
3701
+ cookie = optionsOrCookie;
3702
+ options = cookieOrOptions;
3703
+ config = requestConfig;
3704
+ } else {
3705
+ options = optionsOrCookie;
3706
+ cookie = cookieOrOptions;
3707
+ config = requestConfig;
3708
+ }
3709
+ const { typeMode: _,...validationOptions } = options || {};
3710
+ const rawData = await DouyinData({ ...validateDouyinParams(methodType, validationOptions) }, cookie, config);
3711
+ if (rawData.data === "" || rawData.status_code !== 0) return createErrorResponse(rawData.amagiError, rawData.status_msg || "抖音数据获取失败");
3712
+ return createSuccessResponse(rawData, "获取成功", 200);
3713
+ } catch (error) {
3714
+ const errorMessage = error instanceof Error ? error.message : "未知错误";
3715
+ throw new Error(`抖音数据获取失败: ${errorMessage}`);
3716
+ }
3717
+ }
3718
+ /**
3719
+ * 获取B站数据的核心方法实现
3720
+ */
3721
+ async function getBilibiliData(methodType, optionsOrCookie, cookieOrOptions, requestConfig) {
3722
+ try {
3723
+ let options;
3724
+ let cookie;
3725
+ if (typeof optionsOrCookie === "string") {
3726
+ cookie = optionsOrCookie;
3727
+ options = cookieOrOptions;
3728
+ } else {
3729
+ options = optionsOrCookie;
3730
+ cookie = cookieOrOptions;
3731
+ }
3732
+ const { typeMode: _,...validationOptions } = options || {};
3733
+ const rawData = await fetchBilibili({ ...validateBilibiliParams(methodType, validationOptions) }, cookie);
3734
+ if (rawData.code !== 0) return createErrorResponse(rawData.amagiError, "B站数据获取失败");
3735
+ return createSuccessResponse(rawData, "获取成功", 200);
3736
+ } catch (error) {
3737
+ const errorMessage = error instanceof Error ? error.message : "未知错误";
3738
+ throw new Error(`B站数据获取失败: ${errorMessage}`);
3739
+ }
3740
+ }
3741
+ /**
3742
+ * 获取快手数据的核心方法实现
3743
+ */
3744
+ async function getKuaishouData(methodType, optionsOrCookie, cookieOrOptions, requestConfig) {
3745
+ try {
3746
+ let options;
3747
+ let cookie;
3748
+ if (typeof optionsOrCookie === "string") {
3749
+ cookie = optionsOrCookie;
3750
+ options = cookieOrOptions;
3751
+ } else {
3752
+ options = optionsOrCookie;
3753
+ cookie = cookieOrOptions;
3754
+ }
3755
+ const { typeMode: _,...validationOptions } = options || {};
3756
+ const rawData = await KuaishouData({ ...validateKuaishouParams(methodType, validationOptions) }, cookie);
3757
+ if (rawData.code && Object.values(kuaishouAPIErrorCode).includes(rawData.code)) return createErrorResponse(rawData.amagiError, "快手数据获取失败");
3758
+ return createSuccessResponse(rawData, "获取成功", 200);
3759
+ } catch (error) {
3760
+ const errorMessage = error instanceof Error ? error.message : "未知错误";
3761
+ throw new Error(`快手数据获取失败: ${errorMessage}`);
3762
+ }
3763
+ }
3764
+ /**
3765
+ * 获取小红书数据的核心方法实现
3766
+ */
3767
+ async function getXiaohongshuData(methodType, optionsOrCookie, cookieOrOptions, requestConfig) {
3768
+ try {
3769
+ let options;
3770
+ let cookie;
3771
+ if (typeof optionsOrCookie === "string") {
3772
+ cookie = optionsOrCookie;
3773
+ options = cookieOrOptions;
3774
+ } else {
3775
+ options = optionsOrCookie;
3776
+ cookie = cookieOrOptions;
3777
+ }
3778
+ const { typeMode: _,...validationOptions } = options || {};
3779
+ const rawData = await XiaohongshuData({ ...validateXiaohongshuParams(methodType, validationOptions) }, cookie);
3780
+ if (rawData.code && Object.values(xiaohongshuAPIErrorCode).includes(rawData.code)) return createErrorResponse(rawData.amagiError, "小红书数据获取失败");
3781
+ return createSuccessResponse(rawData, "获取成功", 200);
3782
+ } catch (error) {
3783
+ const errorMessage = error instanceof Error ? error.message : "未知错误";
3784
+ throw new Error(`小红书数据获取失败: ${errorMessage}`);
3785
+ }
3786
+ }
3787
+
3788
+ //#endregion
3789
+ //#region src/platform/bilibili/BilibiliApi.ts
3790
+ /**
3791
+ * 创建B站API方法的通用工厂函数
3792
+ * @template T - B站方法类型键名
3793
+ * @param methodType - 方法类型
3794
+ * @returns 返回配置好的API方法
3795
+ */
3796
+ const createBilibiliApiMethod = (methodType) => {
3797
+ return async (options, cookie) => {
3798
+ return await getBilibiliData(methodType, options, cookie);
3799
+ };
3800
+ };
3801
+ /**
3802
+ * 创建绑定cookie的B站API方法工厂函数
3803
+ * @template T - B站方法类型键名
3804
+ * @param methodType - 方法类型
3805
+ * @param cookie - 绑定的cookie
3806
+ * @returns 返回绑定了cookie的API方法
3807
+ */
3808
+ const createBoundBilibiliApiMethod = (methodType, cookie) => {
3809
+ return async (options) => {
3810
+ return await getBilibiliData(methodType, options, cookie);
3811
+ };
3812
+ };
3813
+ /**
3814
+ * B站相关 API 的命名空间。
3815
+ *
3816
+ * 部分接口可能不需要 Cookie 但建议传递有效的用户 Cookie,以获取更多数据。
3817
+ *
3818
+ * 提供了一系列方法,用于与B站相关的 API 进行交互。
3819
+ *
3820
+ * 每个方法都接受参数和 Cookie,返回 Promise,解析为统一格式的API响应。
3821
+ */
3822
+ const bilibili = {
3823
+ getVideoInfo: createBilibiliApiMethod("单个视频作品数据"),
3824
+ getVideoStream: createBilibiliApiMethod("单个视频下载信息数据"),
3825
+ getComments: createBilibiliApiMethod("评论数据"),
3826
+ getUserProfile: createBilibiliApiMethod("用户主页数据"),
3827
+ getUserDynamic: createBilibiliApiMethod("用户主页动态列表数据"),
3828
+ getEmojiList: createBilibiliApiMethod("Emoji数据"),
3829
+ getBangumiInfo: createBilibiliApiMethod("番剧基本信息数据"),
3830
+ getBangumiStream: createBilibiliApiMethod("番剧下载信息数据"),
3831
+ getDynamicInfo: createBilibiliApiMethod("动态详情数据"),
3832
+ getDynamicCard: createBilibiliApiMethod("动态卡片数据"),
3833
+ getLiveRoomDetail: createBilibiliApiMethod("直播间信息"),
3834
+ getLiveRoomInitInfo: createBilibiliApiMethod("直播间初始化信息"),
3835
+ getLoginBasicInfo: createBilibiliApiMethod("登录基本信息"),
3836
+ getLoginQrcode: createBilibiliApiMethod("申请二维码"),
3837
+ checkQrcodeStatus: createBilibiliApiMethod("二维码状态"),
3838
+ getUserTotalPlayCount: createBilibiliApiMethod("获取UP主总播放量"),
3839
+ convertAvToBv: createBilibiliApiMethod("AV转BV"),
3840
+ convertBvToAv: createBilibiliApiMethod("BV转AV"),
3841
+ getArticleContent: createBilibiliApiMethod("专栏正文内容"),
3842
+ getArticleCard: createBilibiliApiMethod("专栏显示卡片信息"),
3843
+ getArticleInfo: createBilibiliApiMethod("专栏文章基本信息"),
3844
+ getColumnInfo: createBilibiliApiMethod("文集基本信息")
3845
+ };
3846
+ /**
3847
+ * 创建绑定了cookie的B站API对象
3848
+ * @param cookie - 要绑定的cookie(可选)
3849
+ * @returns 绑定了cookie的B站API对象,调用时不需要再传递cookie
3850
+ */
3851
+ const createBoundBilibiliApi = (cookie, requestConfig) => {
3852
+ return {
3853
+ getVideoInfo: createBoundBilibiliApiMethod("单个视频作品数据", cookie),
3854
+ getVideoStream: createBoundBilibiliApiMethod("单个视频下载信息数据", cookie),
3855
+ getComments: createBoundBilibiliApiMethod("评论数据", cookie),
3856
+ getUserProfile: createBoundBilibiliApiMethod("用户主页数据", cookie),
3857
+ getUserDynamic: createBoundBilibiliApiMethod("用户主页动态列表数据", cookie),
3858
+ getEmojiList: createBoundBilibiliApiMethod("Emoji数据", cookie),
3859
+ getBangumiInfo: createBoundBilibiliApiMethod("番剧基本信息数据", cookie),
3860
+ getBangumiStream: createBoundBilibiliApiMethod("番剧下载信息数据", cookie),
3861
+ getDynamicInfo: createBoundBilibiliApiMethod("动态详情数据", cookie),
3862
+ getDynamicCard: createBoundBilibiliApiMethod("动态卡片数据", cookie),
3863
+ getLiveRoomDetail: createBoundBilibiliApiMethod("直播间信息", cookie),
3864
+ getLiveRoomInitInfo: createBoundBilibiliApiMethod("直播间初始化信息", cookie),
3865
+ getLoginBasicInfo: createBoundBilibiliApiMethod("登录基本信息", cookie),
3866
+ getLoginQrcode: createBoundBilibiliApiMethod("申请二维码", cookie),
3867
+ checkQrcodeStatus: createBoundBilibiliApiMethod("二维码状态", cookie),
3868
+ getUserTotalPlayCount: createBoundBilibiliApiMethod("获取UP主总播放量", cookie),
3869
+ convertAvToBv: createBoundBilibiliApiMethod("AV转BV", cookie),
3870
+ convertBvToAv: createBoundBilibiliApiMethod("BV转AV", cookie),
3871
+ getArticleContent: createBoundBilibiliApiMethod("专栏正文内容", cookie),
3872
+ getArticleCard: createBoundBilibiliApiMethod("专栏显示卡片信息", cookie),
3873
+ getArticleInfo: createBoundBilibiliApiMethod("专栏文章基本信息", cookie),
3874
+ getColumnInfo: createBoundBilibiliApiMethod("文集基本信息", cookie)
3875
+ };
3876
+ };
3877
+
3878
+ //#endregion
3879
+ //#region src/utils/errors.ts
3880
+ /**
3881
+ * API错误类
3882
+ */
3883
+ var ApiError = class extends Error {
3884
+ code;
3885
+ platform;
3886
+ /**
3887
+ * 构造API错误
3888
+ * @param message - 错误消息
3889
+ * @param code - 错误代码
3890
+ * @param platform - 平台名称
3891
+ */
3892
+ constructor(message, code = 500, platform = "unknown") {
3893
+ super(message);
3894
+ this.name = "ApiError";
3895
+ this.code = code;
3896
+ this.platform = platform;
3897
+ }
3898
+ };
3899
+ /**
3900
+ * 参数验证错误类
3901
+ */
3902
+ var ValidationError = class ValidationError extends Error {
3903
+ errors;
3904
+ requestPath;
3905
+ /**
3906
+ * 构造参数验证错误
3907
+ * @param message - 错误消息
3908
+ * @param errors - 详细错误信息
3909
+ * @param requestPath - HTTP请求路径
3910
+ */
3911
+ constructor(message, errors, requestPath) {
3912
+ super(message);
3913
+ this.name = "ValidationError";
3914
+ this.errors = errors;
3915
+ this.requestPath = requestPath;
3916
+ }
3917
+ /**
3918
+ * 从Zod错误创建验证错误
3919
+ * @param zodError - Zod验证错误
3920
+ * @param requestPath - HTTP请求路径
3921
+ * @returns 验证错误实例
3922
+ */
3923
+ static fromZodError(zodError, requestPath) {
3924
+ return new ValidationError("参数验证失败", zodError.issues.map((err) => ({
3925
+ field: err.path.join("."),
3926
+ message: err.message
3927
+ })), requestPath);
3928
+ }
3929
+ };
3930
+ /**
3931
+ * 处理错误并返回统一格式
3932
+ * @param error - 错误对象
3933
+ * @param requestPath - HTTP请求路径(可选)
3934
+ * @returns 统一的错误响应格式
3935
+ */
3936
+ const handleError = (error, requestPath) => {
3937
+ if (error instanceof ValidationError) return {
3938
+ code: 400,
3939
+ message: error.message,
3940
+ data: null,
3941
+ errors: error.errors,
3942
+ requestPath: error.requestPath || requestPath
3943
+ };
3944
+ if (error instanceof ApiError) return {
3945
+ code: error.code,
3946
+ message: error.message,
3947
+ data: null,
3948
+ platform: error.platform,
3949
+ requestPath
3950
+ };
3951
+ if (error instanceof zod.z.ZodError) return handleError(ValidationError.fromZodError(error, requestPath), requestPath);
3952
+ return {
3953
+ code: 500,
3954
+ message: error instanceof Error ? error.message : "未知错误",
3955
+ data: null,
3956
+ requestPath
3957
+ };
3958
+ };
3959
+
3960
+ //#endregion
3961
+ //#region src/middleware/validation.ts
3962
+ /**
3963
+ * 创建通用验证中间件
3964
+ * @param validateFn - 验证函数
3965
+ * @param methodType - 方法类型
3966
+ * @returns Express中间件函数
3967
+ */
3968
+ const createValidationMiddleware = (validateFn, methodType) => {
3969
+ return (req, res, next) => {
3970
+ try {
3971
+ req.validatedParams = validateFn(methodType, {
3972
+ ...req.query,
3973
+ ...req.body
3974
+ });
3975
+ next();
3976
+ } catch (error) {
3977
+ const errorResponse = handleError(error, req.originalUrl);
3978
+ res.status(errorResponse.code || 500).json(errorResponse);
3979
+ }
3980
+ };
3981
+ };
3982
+ /**
3983
+ * 创建抖音参数验证中间件
3984
+ * @param methodType - 抖音方法类型
3985
+ * @returns Express中间件函数
3986
+ */
3987
+ const createDouyinValidationMiddleware = (methodType) => createValidationMiddleware(validateDouyinParams, methodType);
3988
+ /**
3989
+ * 创建B站参数验证中间件
3990
+ * @param methodType - B站方法类型
3991
+ * @returns Express中间件函数
3992
+ */
3993
+ const createBilibiliValidationMiddleware = (methodType) => createValidationMiddleware(validateBilibiliParams, methodType);
3994
+ /**
3995
+ * 创建快手参数验证中间件
3996
+ * @param methodType - 快手方法类型
3997
+ * @returns Express中间件函数
3998
+ */
3999
+ const createKuaishouValidationMiddleware = (methodType) => createValidationMiddleware(validateKuaishouParams, methodType);
4000
+ /**
4001
+ * 创建小红书参数验证中间件
4002
+ * @param methodType - 小红书方法类型
4003
+ * @returns Express中间件函数
4004
+ */
4005
+ const createXiaohongshuValidationMiddleware = (methodType) => createValidationMiddleware(validateXiaohongshuParams, methodType);
4006
+
4007
+ //#endregion
4008
+ //#region src/platform/bilibili/routes.ts
4009
+ /**
4010
+ * 创建B站路由处理器
4011
+ * @param dataFetcher - B站数据获取函数
4012
+ * @param methodType - B站方法类型
4013
+ * @param cookie - Cookie字符串
4014
+ * @returns Express路由处理器
4015
+ */
4016
+ const createBilibiliRouteHandler = (dataFetcher, methodType, cookie, requestConfig = getBilibiliDefaultConfig(cookie)) => {
4017
+ return async (req, res) => {
4018
+ try {
4019
+ const result = await dataFetcher(methodType, req.validatedParams, cookie, requestConfig);
4020
+ res.json({
4021
+ ...result,
4022
+ requestPath: req.originalUrl
4023
+ });
4024
+ } catch (error) {
4025
+ const errorResponse = handleError(error);
4026
+ res.status(errorResponse.code || 500).json({
4027
+ ...errorResponse,
4028
+ requestPath: req.originalUrl
4029
+ });
4030
+ }
4031
+ };
4032
+ };
4033
+ /**
4034
+ * 创建B站路由
4035
+ * @param cookie - B站Cookie
4036
+ * @param requestConfig - 可选的请求配置
4037
+ * @returns Express路由器
4038
+ */
4039
+ const createBilibiliRoutes = (cookie, requestConfig = getBilibiliDefaultConfig(cookie)) => {
4040
+ const router = (0, express.Router)();
4041
+ router.get("/fetch_one_video", createBilibiliValidationMiddleware("单个视频作品数据"), createBilibiliRouteHandler(getBilibiliData, "单个视频作品数据", cookie, requestConfig));
4042
+ router.get("/fetch_video_playurl", createBilibiliValidationMiddleware("单个视频下载信息数据"), createBilibiliRouteHandler(getBilibiliData, "单个视频下载信息数据", cookie, requestConfig));
4043
+ router.get("/fetch_work_comments", createBilibiliValidationMiddleware("评论数据"), createBilibiliRouteHandler(getBilibiliData, "评论数据", cookie, requestConfig));
4044
+ router.get("/fetch_user_profile", createBilibiliValidationMiddleware("用户主页数据"), createBilibiliRouteHandler(getBilibiliData, "用户主页数据", cookie, requestConfig));
4045
+ router.get("/fetch_user_dynamic", createBilibiliValidationMiddleware("用户主页动态列表数据"), createBilibiliRouteHandler(getBilibiliData, "用户主页动态列表数据", cookie, requestConfig));
4046
+ router.get("/fetch_emoji_list", createBilibiliValidationMiddleware("Emoji数据"), createBilibiliRouteHandler(getBilibiliData, "Emoji数据", cookie, requestConfig));
4047
+ router.get("/fetch_bangumi_video_info", createBilibiliValidationMiddleware("番剧基本信息数据"), createBilibiliRouteHandler(getBilibiliData, "番剧基本信息数据", cookie, requestConfig));
4048
+ router.get("/fetch_bangumi_video_playurl", createBilibiliValidationMiddleware("番剧下载信息数据"), createBilibiliRouteHandler(getBilibiliData, "番剧下载信息数据", cookie, requestConfig));
4049
+ router.get("/fetch_dynamic_info", createBilibiliValidationMiddleware("动态详情数据"), createBilibiliRouteHandler(getBilibiliData, "动态详情数据", cookie, requestConfig));
4050
+ router.get("/fetch_dynamic_card", createBilibiliValidationMiddleware("动态卡片数据"), createBilibiliRouteHandler(getBilibiliData, "动态卡片数据", cookie, requestConfig));
4051
+ router.get("/fetch_live_room_detail", createBilibiliValidationMiddleware("直播间信息"), createBilibiliRouteHandler(getBilibiliData, "直播间信息", cookie, requestConfig));
4052
+ router.get("/fetch_liveroom_def", createBilibiliValidationMiddleware("直播间初始化信息"), createBilibiliRouteHandler(getBilibiliData, "直播间初始化信息", cookie, requestConfig));
4053
+ router.get("/login_basic_info", createBilibiliValidationMiddleware("登录基本信息"), createBilibiliRouteHandler(getBilibiliData, "登录基本信息", cookie, requestConfig));
4054
+ router.get("/new_login_qrcode", createBilibiliValidationMiddleware("申请二维码"), createBilibiliRouteHandler(getBilibiliData, "申请二维码", cookie, requestConfig));
4055
+ router.get("/check_qrcode", createBilibiliValidationMiddleware("二维码状态"), createBilibiliRouteHandler(getBilibiliData, "二维码状态", cookie, requestConfig));
4056
+ router.get("/fetch_user_full_view", createBilibiliValidationMiddleware("获取UP主总播放量"), createBilibiliRouteHandler(getBilibiliData, "获取UP主总播放量", cookie, requestConfig));
4057
+ router.get("/av_to_bv", createBilibiliValidationMiddleware("AV转BV"), createBilibiliRouteHandler(getBilibiliData, "AV转BV", cookie, requestConfig));
4058
+ router.get("/bv_to_av", createBilibiliValidationMiddleware("BV转AV"), createBilibiliRouteHandler(getBilibiliData, "BV转AV", cookie, requestConfig));
4059
+ router.get("/fetch_article_content", createBilibiliValidationMiddleware("专栏正文内容"), createBilibiliRouteHandler(getBilibiliData, "专栏正文内容", cookie, requestConfig));
4060
+ router.get("/fetch_article_card", createBilibiliValidationMiddleware("专栏显示卡片信息"), createBilibiliRouteHandler(getBilibiliData, "专栏显示卡片信息", cookie, requestConfig));
4061
+ router.get("/fetch_article_info", createBilibiliValidationMiddleware("专栏文章基本信息"), createBilibiliRouteHandler(getBilibiliData, "专栏文章基本信息", cookie, requestConfig));
4062
+ router.get("/fetch_column_info", createBilibiliValidationMiddleware("文集基本信息"), createBilibiliRouteHandler(getBilibiliData, "文集基本信息", cookie, requestConfig));
4063
+ return router;
4064
+ };
4065
+
4066
+ //#endregion
4067
+ //#region src/platform/bilibili/index.ts
4068
+ /** B站相关功能模块 (工具集) */
4069
+ const bilibiliUtils = {
4070
+ sign: {
4071
+ wbi_sign,
4072
+ av2bv,
4073
+ bv2av
4074
+ },
4075
+ bilibiliApiUrls,
4076
+ api: bilibili
4077
+ };
4078
+
4079
+ //#endregion
4080
+ //#region src/platform/douyin/DouyinApi.ts
4081
+ /**
4082
+ * 创建抖音API方法的通用工厂函数
4083
+ * @template T - 抖音方法类型键名
4084
+ * @param methodType - 方法类型
4085
+ * @returns 返回配置好的API方法
4086
+ */
4087
+ const createDouyinApiMethod = (methodType) => {
4088
+ return async (options, cookie, requestConfig) => {
4089
+ return await getDouyinData(methodType, options, cookie, requestConfig);
4090
+ };
4091
+ };
4092
+ /**
4093
+ * 创建绑定cookie的抖音API方法工厂函数
4094
+ * @template T - 抖音方法类型键名
4095
+ * @param methodType - 方法类型
4096
+ * @param cookie - 绑定的cookie
4097
+ * @returns 返回绑定了cookie的API方法
4098
+ */
4099
+ const createBoundDouyinApiMethod = (methodType, cookie, requestConfig) => {
4100
+ return async (options) => {
4101
+ return await getDouyinData(methodType, options, cookie, requestConfig);
4102
+ };
4103
+ };
4104
+ /**
4105
+ * 封装了所有抖音相关的API请求,采用对象化的方式组织。
4106
+ *
4107
+ * 提供了一系列方法,用于与抖音相关的 API 进行交互。
4108
+ *
4109
+ * 每个方法都接受参数和 Cookie,返回 Promise,解析为统一格式的API响应。
4110
+ */
4111
+ const douyin = {
4112
+ getTextWorkInfo: createDouyinApiMethod("文字作品数据"),
4113
+ getWorkInfo: createDouyinApiMethod("聚合解析"),
4114
+ getVideoWorkInfo: createDouyinApiMethod("视频作品数据"),
4115
+ getImageAlbumWorkInfo: createDouyinApiMethod("图集作品数据"),
4116
+ getSlidesWorkInfo: createDouyinApiMethod("合辑作品数据"),
4117
+ getComments: createDouyinApiMethod("评论数据"),
4118
+ getCommentReplies: createDouyinApiMethod("指定评论回复数据"),
4119
+ getUserProfile: createDouyinApiMethod("用户主页数据"),
4120
+ getEmojiList: createDouyinApiMethod("Emoji数据"),
4121
+ getEmojiProList: createDouyinApiMethod("动态表情数据"),
4122
+ getUserVideos: createDouyinApiMethod("用户主页视频列表数据"),
4123
+ getMusicInfo: createDouyinApiMethod("音乐数据"),
4124
+ getSuggestWords: createDouyinApiMethod("热点词数据"),
4125
+ search: createDouyinApiMethod("搜索数据"),
4126
+ getLiveRoomInfo: createDouyinApiMethod("直播间信息数据"),
4127
+ getDanmaku: createDouyinApiMethod("弹幕数据")
4128
+ };
4129
+ /**
4130
+ * 创建绑定了cookie的抖音API对象
4131
+ * @param cookie - 要绑定的cookie
4132
+ * @returns 绑定了cookie的抖音API对象,调用时不需要再传递cookie
4133
+ */
4134
+ const createBoundDouyinApi = (cookie, requestConfig) => {
4135
+ return {
4136
+ getTextWorkInfo: createBoundDouyinApiMethod("文字作品数据", cookie, requestConfig),
4137
+ getWorkInfo: createBoundDouyinApiMethod("聚合解析", cookie, requestConfig),
4138
+ getVideoWorkInfo: createBoundDouyinApiMethod("视频作品数据", cookie, requestConfig),
4139
+ getImageAlbumWorkInfo: createBoundDouyinApiMethod("图集作品数据", cookie, requestConfig),
4140
+ getSlidesWorkInfo: createBoundDouyinApiMethod("合辑作品数据", cookie, requestConfig),
4141
+ getComments: createBoundDouyinApiMethod("评论数据", cookie, requestConfig),
4142
+ getCommentReplies: createBoundDouyinApiMethod("指定评论回复数据", cookie, requestConfig),
4143
+ getUserProfile: createBoundDouyinApiMethod("用户主页数据", cookie, requestConfig),
4144
+ getEmojiList: createBoundDouyinApiMethod("Emoji数据", cookie, requestConfig),
4145
+ getEmojiProList: createBoundDouyinApiMethod("动态表情数据", cookie, requestConfig),
4146
+ getUserVideos: createBoundDouyinApiMethod("用户主页视频列表数据", cookie, requestConfig),
4147
+ getMusicInfo: createBoundDouyinApiMethod("音乐数据", cookie, requestConfig),
4148
+ getSuggestWords: createBoundDouyinApiMethod("热点词数据", cookie, requestConfig),
4149
+ search: createBoundDouyinApiMethod("搜索数据", cookie, requestConfig),
4150
+ getLiveRoomInfo: createBoundDouyinApiMethod("直播间信息数据", cookie, requestConfig),
4151
+ getDanmaku: createBoundDouyinApiMethod("弹幕数据", cookie, requestConfig)
4152
+ };
4153
+ };
4154
+
4155
+ //#endregion
4156
+ //#region src/platform/douyin/routes.ts
4157
+ /**
4158
+ * 创建抖音路由处理器
4159
+ * @param dataFetcher - 抖音数据获取函数
4160
+ * @param methodType - 抖音方法类型
4161
+ * @param cookie - Cookie字符串
4162
+ * @param requestConfig - 可选的请求配置
4163
+ * @returns Express路由处理器
4164
+ */
4165
+ const createDouyinRouteHandler = (dataFetcher, methodType, cookie, requestConfig = getDouyinDefaultConfig(cookie)) => {
4166
+ return async (req, res) => {
4167
+ try {
4168
+ const result = await dataFetcher(methodType, req.validatedParams, cookie, requestConfig);
4169
+ res.json({
4170
+ ...result,
4171
+ requestPath: req.originalUrl
4172
+ });
4173
+ } catch (error) {
4174
+ const errorResponse = handleError(error);
4175
+ res.status(errorResponse.code || 500).json({
4176
+ ...errorResponse,
4177
+ requestPath: req.originalUrl
4178
+ });
4179
+ }
4180
+ };
4181
+ };
4182
+ /**
4183
+ * 创建抖音路由
4184
+ * @param cookie - 抖音Cookie
4185
+ * @param requestConfig - 可选的请求配置
4186
+ * @returns Express路由器
4187
+ */
4188
+ const createDouyinRoutes = (cookie, requestConfig = getDouyinDefaultConfig(cookie)) => {
4189
+ const router = (0, express.Router)();
4190
+ router.get("/fetch_one_work", createDouyinValidationMiddleware("聚合解析"), createDouyinRouteHandler(getDouyinData, "聚合解析", cookie, requestConfig));
4191
+ router.get("/fetch_one_work", createDouyinValidationMiddleware("视频作品数据"), createDouyinRouteHandler(getDouyinData, "视频作品数据", cookie, requestConfig));
4192
+ router.get("/fetch_one_work", createDouyinValidationMiddleware("图集作品数据"), createDouyinRouteHandler(getDouyinData, "图集作品数据", cookie, requestConfig));
4193
+ router.get("/fetch_one_work", createDouyinValidationMiddleware("合辑作品数据"), createDouyinRouteHandler(getDouyinData, "合辑作品数据", cookie, requestConfig));
4194
+ router.get("/fetch_work_comments", createDouyinValidationMiddleware("评论数据"), createDouyinRouteHandler(getDouyinData, "评论数据", cookie, requestConfig));
4195
+ router.get("/fetch_user_info", createDouyinValidationMiddleware("用户主页数据"), createDouyinRouteHandler(getDouyinData, "用户主页数据", cookie, requestConfig));
4196
+ router.get("/fetch_user_post_videos", createDouyinValidationMiddleware("用户主页视频列表数据"), createDouyinRouteHandler(getDouyinData, "用户主页视频列表数据", cookie, requestConfig));
4197
+ router.get("/fetch_search_info", createDouyinValidationMiddleware("搜索数据"), createDouyinRouteHandler(getDouyinData, "搜索数据", cookie, requestConfig));
4198
+ router.get("/fetch_suggest_words", createDouyinValidationMiddleware("热点词数据"), createDouyinRouteHandler(getDouyinData, "热点词数据", cookie, requestConfig));
4199
+ router.get("/fetch_music_work", createDouyinValidationMiddleware("音乐数据"), createDouyinRouteHandler(getDouyinData, "音乐数据", cookie, requestConfig));
4200
+ router.get("/fetch_emoji_list", createDouyinValidationMiddleware("Emoji数据"), createDouyinRouteHandler(getDouyinData, "Emoji数据", cookie, requestConfig));
4201
+ router.get("/fetch_emoji_pro_list", createDouyinValidationMiddleware("动态表情数据"), createDouyinRouteHandler(getDouyinData, "动态表情数据", cookie, requestConfig));
4202
+ router.get("/fetch_user_live_videos", createDouyinValidationMiddleware("直播间信息数据"), createDouyinRouteHandler(getDouyinData, "直播间信息数据", cookie, requestConfig));
4203
+ router.get("/fetch_video_comment_replies", createDouyinValidationMiddleware("指定评论回复数据"), createDouyinRouteHandler(getDouyinData, "指定评论回复数据", cookie, requestConfig));
4204
+ router.get("/fetch_work_danmaku", createDouyinValidationMiddleware("弹幕数据"), createDouyinRouteHandler(getDouyinData, "弹幕数据", cookie, requestConfig));
4205
+ return router;
4206
+ };
4207
+
4208
+ //#endregion
4209
+ //#region src/platform/douyin/index.ts
4210
+ /** 抖音相关功能模块 (工具集) */
4211
+ const douyinUtils = {
4212
+ sign: douyinSign,
4213
+ douyinApiUrls,
4214
+ api: douyin
4215
+ };
4216
+
4217
+ //#endregion
4218
+ //#region src/platform/kuaishou/routes.ts
4219
+ /**
4220
+ * 创建快手路由处理器
4221
+ * @param dataFetcher - 快手数据获取函数
4222
+ * @param methodType - 快手方法类型
4223
+ * @param cookie - Cookie字符串
4224
+ * @returns Express路由处理器
4225
+ */
4226
+ const createKuaishouRouteHandler = (dataFetcher, methodType, cookie, requestConfig = getKuaishouDefaultConfig(cookie)) => {
4227
+ return async (req, res) => {
4228
+ try {
4229
+ const result = await dataFetcher(methodType, req.validatedParams, cookie, requestConfig);
4230
+ res.json({
4231
+ ...result,
4232
+ requestPath: req.originalUrl
4233
+ });
4234
+ } catch (error) {
4235
+ const errorResponse = handleError(error);
4236
+ res.status(errorResponse.code || 500).json({
4237
+ ...errorResponse,
4238
+ requestPath: req.originalUrl
4239
+ });
4240
+ }
4241
+ };
4242
+ };
4243
+ /**
4244
+ * 创建快手路由
4245
+ * @param cookie - 快手Cookie
4246
+ * @param requestConfig - 可选的请求配置
4247
+ * @returns Express路由器
4248
+ */
4249
+ const createKuaishouRoutes = (cookie, requestConfig = getKuaishouDefaultConfig(cookie)) => {
4250
+ const router = (0, express.Router)();
4251
+ router.get("/fetch_one_work", createKuaishouValidationMiddleware("单个视频作品数据"), createKuaishouRouteHandler(getKuaishouData, "单个视频作品数据", cookie, requestConfig));
4252
+ router.get("/fetch_work_comments", createKuaishouValidationMiddleware("评论数据"), createKuaishouRouteHandler(getKuaishouData, "评论数据", cookie, requestConfig));
4253
+ router.get("/fetch_emoji_list", createKuaishouValidationMiddleware("Emoji数据"), createKuaishouRouteHandler(getKuaishouData, "Emoji数据", cookie, requestConfig));
4254
+ return router;
4255
+ };
4256
+
4257
+ //#endregion
4258
+ //#region src/platform/kuaishou/KuaishouApi.ts
4259
+ /**
4260
+ * 创建快手API方法的通用工厂函数
4261
+ * @template T - 快手方法类型键名
4262
+ * @param methodType - 方法类型
4263
+ * @returns 返回配置好的API方法
4264
+ */
4265
+ const createKuaishouApiMethod = (methodType) => {
4266
+ return async (options, cookie) => {
4267
+ return await getKuaishouData(methodType, options, cookie);
4268
+ };
4269
+ };
4270
+ /**
4271
+ * 创建绑定cookie的快手API方法工厂函数
4272
+ * @template T - 快手方法类型键名
4273
+ * @param methodType - 方法类型
4274
+ * @param cookie - 绑定的cookie
4275
+ * @returns 返回绑定了cookie的API方法
4276
+ */
4277
+ const createBoundKuaishouApiMethod = (methodType, cookie) => {
4278
+ return async (options) => {
4279
+ return await getKuaishouData(methodType, options, cookie);
4280
+ };
4281
+ };
4282
+ /**
4283
+ * 快手相关 API 的命名空间。
4284
+ */
4285
+ const kuaishou = {
4286
+ getWorkInfo: createKuaishouApiMethod("单个视频作品数据"),
4287
+ getComments: createKuaishouApiMethod("评论数据"),
4288
+ getEmojiList: createKuaishouApiMethod("Emoji数据")
4289
+ };
4290
+ /**
4291
+ * 创建绑定了cookie的快手API对象
4292
+ * @param cookie - 要绑定的cookie(可选)
4293
+ * @returns 绑定了cookie的快手API对象,调用时不需要再传递cookie
4294
+ */
4295
+ const createBoundKuaishouApi = (cookie, requestConfig) => {
4296
+ return {
4297
+ getWorkInfo: createBoundKuaishouApiMethod("单个视频作品数据", cookie),
4298
+ getComments: createBoundKuaishouApiMethod("评论数据", cookie),
4299
+ getEmojiList: createBoundKuaishouApiMethod("Emoji数据", cookie)
4300
+ };
4301
+ };
4302
+
4303
+ //#endregion
4304
+ //#region src/platform/kuaishou/index.ts
4305
+ /** 快手相关功能模块 (工具集) */
4306
+ const kuaishouUtils = {
4307
+ kuaishouApiUrls,
4308
+ api: kuaishou
4309
+ };
4310
+
4311
+ //#endregion
4312
+ //#region src/platform/xiaohongshu/XiaohongshuApi.ts
4313
+ /**
4314
+ * 创建小红书API方法的通用工厂函数
4315
+ * @template T - 小红书方法类型键名
4316
+ * @param methodType - 方法类型
4317
+ * @returns 返回配置好的API方法
4318
+ */
4319
+ const createXiaohongshuApiMethod = (methodType) => {
4320
+ return async (options, cookie, requestConfig) => {
4321
+ return await getXiaohongshuData(methodType, options, cookie, requestConfig);
4322
+ };
4323
+ };
4324
+ /**
4325
+ * 创建绑定cookie的小红书API方法工厂函数
4326
+ * @template T - 小红书方法类型键名
4327
+ * @param methodType - 方法类型
4328
+ * @param cookie - 绑定的cookie
4329
+ * @returns 返回绑定了cookie的API方法
4330
+ */
4331
+ const createBoundXiaohongshuApiMethod = (methodType, cookie, requestConfig) => {
4332
+ return async (options) => {
4333
+ return await getXiaohongshuData(methodType, options, cookie, requestConfig);
4334
+ };
4335
+ };
4336
+ /**
4337
+ * 封装了所有小红书相关的API请求,采用对象化的方式组织。
4338
+ *
4339
+ * 提供了一系列方法,用于与小红书相关的 API 进行交互。
4340
+ *
4341
+ * 每个方法都接受参数和 Cookie,返回 Promise,解析为统一格式的API响应。
4342
+ */
4343
+ const xiaohongshu = {
4344
+ getHomeFeed: createXiaohongshuApiMethod("首页推荐数据"),
4345
+ getNote: createXiaohongshuApiMethod("单个笔记数据"),
4346
+ getComments: createXiaohongshuApiMethod("评论数据"),
4347
+ getUser: createXiaohongshuApiMethod("用户数据"),
4348
+ getUserNotes: createXiaohongshuApiMethod("用户笔记数据"),
4349
+ getSearchNotes: createXiaohongshuApiMethod("搜索笔记"),
4350
+ getEmojiList: createXiaohongshuApiMethod("表情列表")
4351
+ };
4352
+ /**
4353
+ * 创建绑定了cookie的小红书API对象
4354
+ * @param cookie - 要绑定的cookie
4355
+ * @returns 绑定了cookie的小红书API对象,调用时不需要再传递cookie
4356
+ */
4357
+ const createBoundXiaohongshuApi = (cookie, requestConfig) => {
4358
+ return {
4359
+ getHomeFeed: createBoundXiaohongshuApiMethod("首页推荐数据", cookie, requestConfig),
4360
+ getNote: createBoundXiaohongshuApiMethod("单个笔记数据", cookie, requestConfig),
4361
+ getComments: createBoundXiaohongshuApiMethod("评论数据", cookie, requestConfig),
4362
+ getUser: createBoundXiaohongshuApiMethod("用户数据", cookie, requestConfig),
4363
+ getUserNotes: createBoundXiaohongshuApiMethod("用户笔记数据", cookie, requestConfig),
4364
+ getSearchNotes: createBoundXiaohongshuApiMethod("搜索笔记", cookie, requestConfig),
4365
+ getEmojiList: createBoundXiaohongshuApiMethod("表情列表", cookie, requestConfig)
4366
+ };
4367
+ };
4368
+
4369
+ //#endregion
4370
+ //#region src/platform/xiaohongshu/routes.ts
4371
+ /**
4372
+ * 创建小红书路由处理器
4373
+ * @param dataFetcher - 小红书数据获取函数
4374
+ * @param methodType - 小红书方法类型
4375
+ * @param cookie - Cookie字符串
4376
+ * @param requestConfig - 可选的请求配置
4377
+ * @returns Express路由处理器
4378
+ */
4379
+ const createXiaohongshuRouteHandler = (dataFetcher, methodType, cookie, requestConfig = getXiaohongshuDefaultConfig(cookie)) => {
4380
+ return async (req, res) => {
4381
+ try {
4382
+ const result = await dataFetcher(methodType, req.validatedParams, cookie, requestConfig);
4383
+ res.json({
4384
+ ...result,
4385
+ requestPath: req.originalUrl
4386
+ });
4387
+ } catch (error) {
4388
+ const errorResponse = handleError(error);
4389
+ res.status(errorResponse.code || 500).json({
4390
+ ...errorResponse,
4391
+ requestPath: req.originalUrl
4392
+ });
4393
+ }
4394
+ };
4395
+ };
4396
+ /**
4397
+ * 创建小红书路由
4398
+ * @param cookie - 小红书Cookie
4399
+ * @param requestConfig - 可选的请求配置
4400
+ * @returns Express路由器
4401
+ */
4402
+ const createXiaohongshuRoutes = (cookie, requestConfig = getXiaohongshuDefaultConfig(cookie)) => {
4403
+ const router = (0, express.Router)();
4404
+ router.get("/fetch_home_feed", createXiaohongshuValidationMiddleware("首页推荐数据"), createXiaohongshuRouteHandler(getXiaohongshuData, "首页推荐数据", cookie, requestConfig));
4405
+ router.get("/fetch_one_note", createXiaohongshuValidationMiddleware("单个笔记数据"), createXiaohongshuRouteHandler(getXiaohongshuData, "单个笔记数据", cookie, requestConfig));
4406
+ router.get("/fetch_note_comments", createXiaohongshuValidationMiddleware("评论数据"), createXiaohongshuRouteHandler(getXiaohongshuData, "评论数据", cookie, requestConfig));
4407
+ router.get("/fetch_user_profile", createXiaohongshuValidationMiddleware("用户数据"), createXiaohongshuRouteHandler(getXiaohongshuData, "用户数据", cookie, requestConfig));
4408
+ router.get("/fetch_user_notes", createXiaohongshuValidationMiddleware("用户笔记数据"), createXiaohongshuRouteHandler(getXiaohongshuData, "用户笔记数据", cookie, requestConfig));
4409
+ router.get("/fetch_emoji_list", createXiaohongshuValidationMiddleware("表情列表"), createXiaohongshuRouteHandler(getXiaohongshuData, "表情列表", cookie, requestConfig));
4410
+ router.get("/fetch_search_notes", createXiaohongshuValidationMiddleware("搜索笔记"), createXiaohongshuRouteHandler(getXiaohongshuData, "搜索笔记", cookie, requestConfig));
4411
+ return router;
4412
+ };
4413
+
4414
+ //#endregion
4415
+ //#region src/platform/xiaohongshu/index.ts
4416
+ /** 小红书相关功能模块 (工具集) */
4417
+ const xiaohongshuUtils = {
4418
+ sign: xiaohongshuSign,
4419
+ xiaohongshuApiUrls,
4420
+ api: xiaohongshu
4421
+ };
4422
+
4423
+ //#endregion
4424
+ //#region src/server/index.ts
4425
+ /**
4426
+ * 创建Amagi客户端实例
4427
+ * @param options - 客户端配置选项,包含Cookie和请求配置
4428
+ * @returns 包含数据获取方法、服务器启动方法、绑定Cookie的平台工具集和API对象的对象
4429
+ */
4430
+ const createAmagiClient = (options) => {
4431
+ const douyinCookie = options?.cookies?.douyin ?? "";
4432
+ const bilibiliCookie = options?.cookies?.bilibili ?? "";
4433
+ const kuaishouCookie = options?.cookies?.kuaishou ?? "";
4434
+ const xiaohongshuCookie = options?.cookies?.xiaohongshu ?? "";
4435
+ const requestConfig = options?.request ?? {};
4436
+ /**
4437
+ * 启动本地HTTP服务
4438
+ * @param port - 监听端口,默认4567
4439
+ * @returns Express应用实例
4440
+ */
4441
+ const startServer = (port = 4567) => {
4442
+ const app = (0, express.default)();
4443
+ app.use(express.default.json());
4444
+ app.use(express.default.urlencoded({ extended: true }));
4445
+ app.get("/", (_req, res) => {
4446
+ res.redirect(301, "https://amagi.apifox.cn");
4447
+ });
4448
+ app.get("/docs", (_req, res) => {
4449
+ res.redirect(301, "https://amagi.apifox.cn");
4450
+ });
4451
+ app.use("/api/douyin", createDouyinRoutes(douyinCookie, requestConfig));
4452
+ app.use("/api/bilibili", createBilibiliRoutes(bilibiliCookie, requestConfig));
4453
+ app.use("/api/kuaishou", createKuaishouRoutes(kuaishouCookie, requestConfig));
4454
+ app.use("/api/xiaohongshu", createXiaohongshuRoutes(xiaohongshuCookie, requestConfig));
4455
+ app.listen(port, "::", () => {
4456
+ logger.mark(`Amagi server listening on ${logger.green(`http://localhost:${port}`)} ${logger.yellow("API docs: https://amagi.apifox.cn ")}`);
4457
+ });
4458
+ return app;
4459
+ };
4460
+ /**
4461
+ * 获取抖音数据
4462
+ * @param methodType - 请求数据类型
4463
+ * @param options - 请求参数
4464
+ * @returns 返回包装在data字段中的数据
4465
+ */
4466
+ const getDouyinDataWithCookie = async (methodType, options$1) => {
4467
+ return await getDouyinData(methodType, options$1, douyinCookie, requestConfig);
4468
+ };
4469
+ /**
4470
+ * 获取B站数据
4471
+ * @param methodType - 请求数据类型
4472
+ * @param options - 请求参数
4473
+ * @returns 返回包装在data字段中的数据
4474
+ */
4475
+ const getBilibiliDataWithCookie = async (methodType, options$1) => {
4476
+ return await getBilibiliData(methodType, options$1, bilibiliCookie, requestConfig);
4477
+ };
4478
+ /**
4479
+ * 获取快手数据
4480
+ * @param methodType - 请求数据类型
4481
+ * @param options - 请求参数
4482
+ * @returns 返回包装在data字段中的数据
4483
+ */
4484
+ const getKuaishouDataWithCookie = async (methodType, options$1) => {
4485
+ return await getKuaishouData(methodType, options$1, kuaishouCookie, requestConfig);
4486
+ };
4487
+ /**
4488
+ * 获取小红书数据
4489
+ * @param methodType - 请求数据类型
4490
+ * @param options - 请求参数
4491
+ * @returns 返回包装在data字段中的数据
4492
+ */
4493
+ const getXiaohongshuDataWithCookie = async (methodType, options$1) => {
4494
+ return await getXiaohongshuData(methodType, options$1, xiaohongshuCookie, requestConfig);
4495
+ };
4496
+ return {
4497
+ startServer,
4498
+ getDouyinData: getDouyinDataWithCookie,
4499
+ getBilibiliData: getBilibiliDataWithCookie,
4500
+ getKuaishouData: getKuaishouDataWithCookie,
4501
+ getXiaohongshuData: getXiaohongshuDataWithCookie,
4502
+ douyin: {
4503
+ ...douyinUtils,
4504
+ api: createBoundDouyinApi(douyinCookie, requestConfig)
4505
+ },
4506
+ bilibili: {
4507
+ ...bilibiliUtils,
4508
+ api: createBoundBilibiliApi(bilibiliCookie, requestConfig)
4509
+ },
4510
+ kuaishou: {
4511
+ ...kuaishouUtils,
4512
+ api: createBoundKuaishouApi(kuaishouCookie, requestConfig)
4513
+ },
4514
+ xiaohongshu: {
4515
+ ...xiaohongshuUtils,
4516
+ api: createBoundXiaohongshuApi(xiaohongshuCookie, requestConfig)
4517
+ }
4518
+ };
4519
+ };
4520
+
4521
+ //#endregion
4522
+ //#region src/types/ReturnDataType/Bilibili/DynamicInfo.ts
4523
+ let DynamicType = /* @__PURE__ */ function(DynamicType$1) {
4524
+ DynamicType$1["AV"] = "DYNAMIC_TYPE_AV";
4525
+ DynamicType$1["DRAW"] = "DYNAMIC_TYPE_DRAW";
4526
+ DynamicType$1["WORD"] = "DYNAMIC_TYPE_WORD";
4527
+ DynamicType$1["LIVE_RCMD"] = "DYNAMIC_TYPE_LIVE_RCMD";
4528
+ DynamicType$1["FORWARD"] = "DYNAMIC_TYPE_FORWARD";
4529
+ DynamicType$1["ARTICLE"] = "DYNAMIC_TYPE_ARTICLE";
4530
+ return DynamicType$1;
4531
+ }({});
4532
+
4533
+ //#endregion
4534
+ //#region src/types/ReturnDataType/Bilibili/Dynamic/index.ts
4535
+ /**
4536
+ * 转发动态种子动态主体类型枚举
4537
+ */
4538
+ let MajorType = /* @__PURE__ */ function(MajorType$1) {
4539
+ /** 动态失效 */
4540
+ MajorType$1["NONE"] = "MAJOR_TYPE_NONE";
4541
+ /** 图文动态 */
4542
+ MajorType$1["OPUS"] = "MAJOR_TYPE_OPUS";
4543
+ /** 视频 */
4544
+ MajorType$1["ARCHIVE"] = "MAJOR_TYPE_ARCHIVE";
4545
+ /** 剧集更新 */
4546
+ MajorType$1["PGC"] = "MAJOR_TYPE_PGC";
4547
+ /** 课程 */
4548
+ MajorType$1["COURSES"] = "MAJOR_TYPE_COURSES";
4549
+ /** 带图动态 */
4550
+ MajorType$1["DRAW"] = "MAJOR_TYPE_DRAW";
4551
+ /** 文章 */
4552
+ MajorType$1["ARTICLE"] = "MAJOR_TYPE_ARTICLE";
4553
+ /** 音频更新 */
4554
+ MajorType$1["MUSIC"] = "MAJOR_TYPE_MUSIC";
4555
+ /** 一般类型 */
4556
+ MajorType$1["COMMON"] = "MAJOR_TYPE_COMMON";
4557
+ /** 直播间分享 */
4558
+ MajorType$1["LIVE"] = "MAJOR_TYPE_LIVE";
4559
+ /** 媒体列表 */
4560
+ MajorType$1["MEDIALIST"] = "MAJOR_TYPE_MEDIALIST";
4561
+ /** 小程序 */
4562
+ MajorType$1["APPLET"] = "MAJOR_TYPE_APPLET";
4563
+ /** 订阅 */
4564
+ MajorType$1["SUBSCRIPTION"] = "MAJOR_TYPE_SUBSCRIPTION";
4565
+ /** 直播状态 */
4566
+ MajorType$1["LIVE_RCMD"] = "MAJOR_TYPE_LIVE_RCMD";
4567
+ /** 合集更新 */
4568
+ MajorType$1["UGC_SEASON"] = "MAJOR_TYPE_UGC_SEASON";
4569
+ /** 新订阅 */
4570
+ MajorType$1["SUBSCRIPTION_NEW"] = "MAJOR_TYPE_SUBSCRIPTION_NEW";
4571
+ /** 充电相关 */
4572
+ MajorType$1["UPOWER_COMMON"] = "MAJOR_TYPE_UPOWER_COMMON";
4573
+ return MajorType$1;
4574
+ }({});
4575
+ /**
4576
+ * 相关内容卡片类型枚举
4577
+ * 用于标识动态中附加的相关内容卡片的类型
4578
+ */
4579
+ let AdditionalType = /* @__PURE__ */ function(AdditionalType$1) {
4580
+ /** 无相关内容 */
4581
+ AdditionalType$1["NONE"] = "ADDITIONAL_TYPE_NONE";
4582
+ /** 剧集相关 */
4583
+ AdditionalType$1["PGC"] = "ADDITIONAL_TYPE_PGC";
4584
+ /** 商品信息 */
4585
+ AdditionalType$1["GOODS"] = "ADDITIONAL_TYPE_GOODS";
4586
+ /** 投票 */
4587
+ AdditionalType$1["VOTE"] = "ADDITIONAL_TYPE_VOTE";
4588
+ /** 一般类型 */
4589
+ AdditionalType$1["COMMON"] = "ADDITIONAL_TYPE_COMMON";
4590
+ /** 比赛信息 */
4591
+ AdditionalType$1["MATCH"] = "ADDITIONAL_TYPE_MATCH";
4592
+ /** UP主推荐 */
4593
+ AdditionalType$1["UP_RCMD"] = "ADDITIONAL_TYPE_UP_RCMD";
4594
+ /** 视频跳转 */
4595
+ AdditionalType$1["UGC"] = "ADDITIONAL_TYPE_UGC";
4596
+ /** 直播预约 */
4597
+ AdditionalType$1["RESERVE"] = "ADDITIONAL_TYPE_RESERVE";
4598
+ /** 充电专属抽奖 */
4599
+ AdditionalType$1["UPOWER_LOTTERY"] = "ADDITIONAL_TYPE_UPOWER_LOTTERY";
4600
+ return AdditionalType$1;
4601
+ }({});
4602
+
4603
+ //#endregion
4604
+ //#region src/index.ts
4605
+ /**
4606
+ * @deprecated 请使用 createAmagiClient 替代
4607
+ */
4608
+ const amagiClient = createAmagiClient;
4609
+ /**
4610
+ * 创建一个新的 amagi 客户端实例
4611
+ * 用于创建和初始化一个新的 amagi 客户端实例,支持通过 new 关键字或函数调用方式使用
4612
+ * @param options - cookies 配置选项,用于设置客户端的 cookies 相关参数
4613
+ * @returns 返回一个新的 amagi 客户端实例
4614
+ */
4615
+ function CreateAmagiApp(options = {}) {
4616
+ if (!(this instanceof CreateAmagiApp)) return createAmagiClient(options);
4617
+ return createAmagiClient(options);
4618
+ }
4619
+ CreateAmagiApp.douyin = douyinUtils;
4620
+ CreateAmagiApp.bilibili = bilibiliUtils;
4621
+ CreateAmagiApp.kuaishou = kuaishouUtils;
4622
+ CreateAmagiApp.xiaohongshu = xiaohongshuUtils;
4623
+ CreateAmagiApp.getDouyinData = getDouyinData;
4624
+ CreateAmagiApp.getBilibiliData = getBilibiliData;
4625
+ CreateAmagiApp.getKuaishouData = getKuaishouData;
4626
+ CreateAmagiApp.getXiaohongshuData = getXiaohongshuData;
4627
+ /** After instantiation, it can interact with the specified platform API to quickly obtain data. */
4628
+ const CreateApp = CreateAmagiApp;
4629
+ /** After instantiation, it can interact with the specified platform API to quickly obtain data. */
4630
+ const Client = CreateApp;
4631
+ const amagi = Client;
4632
+
4633
+ //#endregion
4634
+ exports.AdditionalType = AdditionalType;
4635
+ exports.ApiError = ApiError;
4636
+ exports.BilibiliArticleCardParamsSchema = BilibiliArticleCardParamsSchema;
4637
+ exports.BilibiliArticleInfoParamsSchema = BilibiliArticleInfoParamsSchema;
4638
+ exports.BilibiliArticleParamsSchema = BilibiliArticleParamsSchema;
4639
+ exports.BilibiliAv2BvParamsSchema = BilibiliAv2BvParamsSchema;
4640
+ exports.BilibiliBangumiInfoParamsSchema = BilibiliBangumiInfoParamsSchema;
4641
+ exports.BilibiliBangumiStreamParamsSchema = BilibiliBangumiStreamParamsSchema;
4642
+ exports.BilibiliBv2AvParamsSchema = BilibiliBv2AvParamsSchema;
4643
+ exports.BilibiliColumnInfoParamsSchema = BilibiliColumnInfoParamsSchema;
4644
+ exports.BilibiliCommentParamsSchema = BilibiliCommentParamsSchema;
4645
+ exports.BilibiliDynamicParamsSchema = BilibiliDynamicParamsSchema;
4646
+ exports.BilibiliEmojiParamsSchema = BilibiliEmojiParamsSchema;
4647
+ exports.BilibiliLiveParamsSchema = BilibiliLiveParamsSchema;
4648
+ exports.BilibiliLoginParamsSchema = BilibiliLoginParamsSchema;
4649
+ exports.BilibiliQrcodeParamsSchema = BilibiliQrcodeParamsSchema;
4650
+ exports.BilibiliQrcodeStatusParamsSchema = BilibiliQrcodeStatusParamsSchema;
4651
+ exports.BilibiliUserParamsSchema = BilibiliUserParamsSchema;
4652
+ exports.BilibiliValidationSchemas = BilibiliValidationSchemas;
4653
+ exports.BilibiliVideoDownloadParamsSchema = BilibiliVideoDownloadParamsSchema;
4654
+ exports.BilibiliVideoParamsSchema = BilibiliVideoParamsSchema;
4655
+ exports.CreateApp = CreateApp;
4656
+ exports.DouyinCommentParamsSchema = DouyinCommentParamsSchema;
4657
+ exports.DouyinCommentReplyParamsSchema = DouyinCommentReplyParamsSchema;
4658
+ exports.DouyinDanmakuParamsSchema = DouyinDanmakuParamsSchema;
4659
+ exports.DouyinEmojiListParamsSchema = DouyinEmojiListParamsSchema;
4660
+ exports.DouyinEmojiProParamsSchema = DouyinEmojiProParamsSchema;
4661
+ exports.DouyinMusicParamsSchema = DouyinMusicParamsSchema;
4662
+ exports.DouyinQrcodeParamsSchema = DouyinQrcodeParamsSchema;
4663
+ exports.DouyinSearchParamsSchema = DouyinSearchParamsSchema;
4664
+ exports.DouyinUserParamsSchema = DouyinUserParamsSchema;
4665
+ exports.DouyinValidationSchemas = DouyinValidationSchemas;
4666
+ exports.DouyinWorkParamsSchema = DouyinWorkParamsSchema;
4667
+ exports.DynamicType = DynamicType;
4668
+ exports.KuaishouCommentParamsSchema = KuaishouCommentParamsSchema;
4669
+ exports.KuaishouEmojiParamsSchema = KuaishouEmojiParamsSchema;
4670
+ exports.KuaishouValidationSchemas = KuaishouValidationSchemas;
4671
+ exports.KuaishouVideoParamsSchema = KuaishouVideoParamsSchema;
4672
+ exports.MajorType = MajorType;
4673
+ exports.ValidationError = ValidationError;
4674
+ exports.XiaohongshuValidationSchemas = XiaohongshuValidationSchemas;
4675
+ exports.amagi = amagi;
4676
+ exports.amagiClient = amagiClient;
4677
+ exports.av2bv = av2bv;
4678
+ exports.bilibili = bilibili;
4679
+ exports.bilibiliApiUrls = bilibiliApiUrls;
4680
+ exports.bilibiliErrorCodeMap = bilibiliErrorCodeMap;
4681
+ exports.bilibiliUtils = bilibiliUtils;
4682
+ exports.bv2av = bv2av;
4683
+ exports.createAmagiClient = createAmagiClient;
4684
+ exports.createBilibiliRoutes = createBilibiliRoutes;
4685
+ exports.createBoundBilibiliApi = createBoundBilibiliApi;
4686
+ exports.createBoundDouyinApi = createBoundDouyinApi;
4687
+ exports.createBoundKuaishouApi = createBoundKuaishouApi;
4688
+ exports.createBoundXiaohongshuApi = createBoundXiaohongshuApi;
4689
+ exports.createDouyinRoutes = createDouyinRoutes;
4690
+ exports.createErrorResponse = createErrorResponse;
4691
+ exports.createKuaishouRoutes = createKuaishouRoutes;
4692
+ exports.createSuccessResponse = createSuccessResponse;
4693
+ exports.createXiaohongshuRoutes = createXiaohongshuRoutes;
4694
+ exports.default = Client;
4695
+ exports.douyin = douyin;
4696
+ exports.douyinApiUrls = douyinApiUrls;
4697
+ exports.douyinSign = douyinSign;
4698
+ exports.douyinUtils = douyinUtils;
4699
+ exports.fetchData = fetchData;
4700
+ exports.fetchResponse = fetchResponse;
4701
+ exports.getBilibiliData = getBilibiliData;
4702
+ exports.getDouyinData = getDouyinData;
4703
+ exports.getHeadersAndData = getHeadersAndData;
4704
+ exports.getKuaishouData = getKuaishouData;
4705
+ exports.handleError = handleError;
4706
+ exports.httpLogger = httpLogger;
4707
+ exports.kuaishou = kuaishou;
4708
+ exports.kuaishouApiUrls = kuaishouApiUrls;
4709
+ exports.kuaishouUtils = kuaishouUtils;
4710
+ exports.logMiddleware = logMiddleware;
4711
+ exports.logger = logger;
4712
+ exports.qtparam = qtparam;
4713
+ exports.registerBilibiliRoutes = createBilibiliRoutes;
4714
+ exports.registerDouyinRoutes = createDouyinRoutes;
4715
+ exports.registerKuaishouRoutes = createKuaishouRoutes;
4716
+ exports.registerXiaohongshuRoutes = createXiaohongshuRoutes;
4717
+ exports.validateBilibiliParams = validateBilibiliParams;
4718
+ exports.validateDouyinParams = validateDouyinParams;
4719
+ exports.validateKuaishouParams = validateKuaishouParams;
4720
+ exports.validateXiaohongshuParams = validateXiaohongshuParams;
4721
+ exports.wbi_sign = wbi_sign;
4722
+ exports.xiaohongshu = xiaohongshu;
4723
+ exports.xiaohongshuApiUrls = xiaohongshuApiUrls;
4724
+ exports.xiaohongshuSign = xiaohongshuSign;
4725
+ exports.xiaohongshuUtils = xiaohongshuUtils;