@ikenxuan/amagi 5.6.3 → 5.7.0

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