@ikenxuan/amagi 5.12.1 → 5.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/default/index.cjs +447 -280
- package/dist/default/index.d.cts +145 -31
- package/dist/default/index.d.cts.map +1 -0
- package/dist/default/index.d.ts +145 -31
- package/dist/default/index.d.ts.map +1 -0
- package/dist/default/index.js +443 -278
- package/dist/default/index.js.map +1 -0
- package/package.json +17 -16
package/dist/default/index.cjs
CHANGED
|
@@ -1,25 +1,39 @@
|
|
|
1
1
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
2
2
|
const require_chunk = require('../chunk-CbDLau6x.cjs');
|
|
3
|
-
let node_fs = require("node:fs");
|
|
4
|
-
node_fs = require_chunk.__toESM(node_fs);
|
|
5
3
|
let node_path = require("node:path");
|
|
6
4
|
node_path = require_chunk.__toESM(node_path);
|
|
7
5
|
let node_url = require("node:url");
|
|
8
6
|
node_url = require_chunk.__toESM(node_url);
|
|
7
|
+
let node_fs = require("node:fs");
|
|
8
|
+
node_fs = require_chunk.__toESM(node_fs);
|
|
9
9
|
let chalk = require("chalk");
|
|
10
|
-
let log4js = require("log4js");
|
|
11
|
-
log4js = require_chunk.__toESM(log4js);
|
|
12
10
|
let axios = require("axios");
|
|
13
11
|
axios = require_chunk.__toESM(axios);
|
|
14
12
|
let zod = require("zod");
|
|
15
13
|
zod = require_chunk.__toESM(zod);
|
|
14
|
+
let __ikenxuan_xhshow_ts = require("@ikenxuan/xhshow-ts");
|
|
15
|
+
let protobufjs = require("protobufjs");
|
|
16
|
+
protobufjs = require_chunk.__toESM(protobufjs);
|
|
16
17
|
let node_crypto = require("node:crypto");
|
|
17
18
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
18
|
-
let __ikenxuan_xhshow_ts = require("@ikenxuan/xhshow-ts");
|
|
19
19
|
let express = require("express");
|
|
20
20
|
express = require_chunk.__toESM(express);
|
|
21
21
|
|
|
22
22
|
//#region src/model/logger.ts
|
|
23
|
+
/**
|
|
24
|
+
* 动态获取 log4js 库
|
|
25
|
+
* 优先使用 node-karin/log4js (Karin 环境)
|
|
26
|
+
* 失败则回退到 log4js (独立环境,通过别名映射到 @karinjs/log4js)
|
|
27
|
+
*/
|
|
28
|
+
const getLog4js = async () => {
|
|
29
|
+
try {
|
|
30
|
+
return (await import("node-karin/log4js")).default;
|
|
31
|
+
} catch {
|
|
32
|
+
const lib = await import("log4js");
|
|
33
|
+
return lib.default || lib;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const log4jsPromise = getLog4js();
|
|
23
37
|
/** 获取包的绝对路径 */
|
|
24
38
|
const getPackageLogsPath = () => {
|
|
25
39
|
const currentFileUrl = require("url").pathToFileURL(__filename).href;
|
|
@@ -29,7 +43,11 @@ const getPackageLogsPath = () => {
|
|
|
29
43
|
if (node_fs.default.existsSync(node_path.default.join(packageRoot, "package.json"))) break;
|
|
30
44
|
packageRoot = node_path.default.dirname(packageRoot);
|
|
31
45
|
}
|
|
32
|
-
|
|
46
|
+
const logsDir = node_path.default.join(packageRoot, "logs");
|
|
47
|
+
try {
|
|
48
|
+
if (!node_fs.default.existsSync(logsDir)) node_fs.default.mkdirSync(logsDir, { recursive: true });
|
|
49
|
+
} catch (error) {}
|
|
50
|
+
return logsDir;
|
|
33
51
|
};
|
|
34
52
|
const logsPath = getPackageLogsPath();
|
|
35
53
|
/** 获取日志级别,优先使用环境变量,默认为 info */
|
|
@@ -37,59 +55,65 @@ const getLogLevel = () => {
|
|
|
37
55
|
return process.env.LOG_LEVEL ?? "info";
|
|
38
56
|
};
|
|
39
57
|
const currentLogLevel = getLogLevel();
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
58
|
+
/** 初始化 logger 配置 */
|
|
59
|
+
const initLogger = () => {
|
|
60
|
+
log4jsPromise.then((log4js) => {
|
|
61
|
+
log4js.configure({
|
|
62
|
+
appenders: {
|
|
63
|
+
console: {
|
|
64
|
+
type: "stdout",
|
|
65
|
+
layout: {
|
|
66
|
+
type: "pattern",
|
|
67
|
+
pattern: "%[[amagi][%d{hh:mm:ss.SSS}][%4.4p]%] %m"
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
command: {
|
|
71
|
+
type: "dateFile",
|
|
72
|
+
filename: node_path.default.join(logsPath, "application", "command"),
|
|
73
|
+
pattern: "yyyy-MM-dd.log",
|
|
74
|
+
numBackups: 15,
|
|
75
|
+
alwaysIncludePattern: true,
|
|
76
|
+
layout: {
|
|
77
|
+
type: "pattern",
|
|
78
|
+
pattern: "[%d{hh:mm:ss.SSS}][%4.4p] %m"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
httpConsole: {
|
|
82
|
+
type: "stdout",
|
|
83
|
+
layout: {
|
|
84
|
+
type: "pattern",
|
|
85
|
+
pattern: "%[[amagi][%d{hh:mm:ss.SSS}][HTTP]%] %m"
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
httpRequest: {
|
|
89
|
+
type: "dateFile",
|
|
90
|
+
filename: node_path.default.join(logsPath, "http", "requests"),
|
|
91
|
+
pattern: "yyyy-MM-dd.log",
|
|
92
|
+
numBackups: 30,
|
|
93
|
+
alwaysIncludePattern: true,
|
|
94
|
+
layout: {
|
|
95
|
+
type: "pattern",
|
|
96
|
+
pattern: "[%d{hh:mm:ss.SSS}][%4.4p] %m"
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
categories: {
|
|
101
|
+
default: {
|
|
102
|
+
appenders: ["console", "command"],
|
|
103
|
+
level: currentLogLevel
|
|
104
|
+
},
|
|
105
|
+
http: {
|
|
106
|
+
appenders: ["httpConsole", "httpRequest"],
|
|
107
|
+
level: "debug"
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
pm2: true
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
};
|
|
91
114
|
var CustomLogger = class {
|
|
92
115
|
logger;
|
|
116
|
+
queue = [];
|
|
93
117
|
chalk;
|
|
94
118
|
red;
|
|
95
119
|
green;
|
|
@@ -100,7 +124,10 @@ var CustomLogger = class {
|
|
|
100
124
|
white;
|
|
101
125
|
gray;
|
|
102
126
|
constructor(name) {
|
|
103
|
-
|
|
127
|
+
log4jsPromise.then((log4js) => {
|
|
128
|
+
this.logger = log4js.getLogger(name);
|
|
129
|
+
this.flush();
|
|
130
|
+
});
|
|
104
131
|
this.chalk = new chalk.Chalk();
|
|
105
132
|
this.red = this.chalk.red;
|
|
106
133
|
this.green = this.chalk.green;
|
|
@@ -111,20 +138,31 @@ var CustomLogger = class {
|
|
|
111
138
|
this.white = this.chalk.white;
|
|
112
139
|
this.gray = this.chalk.gray;
|
|
113
140
|
}
|
|
141
|
+
flush() {
|
|
142
|
+
if (!this.logger) return;
|
|
143
|
+
while (this.queue.length) {
|
|
144
|
+
const [method, args] = this.queue.shift();
|
|
145
|
+
this.logger[method](...args);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
proxy(method, args) {
|
|
149
|
+
if (this.logger) this.logger[method](...args);
|
|
150
|
+
else this.queue.push([method, args]);
|
|
151
|
+
}
|
|
114
152
|
info(message, ...args) {
|
|
115
|
-
this.
|
|
153
|
+
this.proxy("info", [message, ...args]);
|
|
116
154
|
}
|
|
117
155
|
warn(message, ...args) {
|
|
118
|
-
this.
|
|
156
|
+
this.proxy("warn", [message, ...args]);
|
|
119
157
|
}
|
|
120
158
|
error(message, ...args) {
|
|
121
|
-
this.
|
|
159
|
+
this.proxy("error", [message, ...args]);
|
|
122
160
|
}
|
|
123
161
|
mark(message, ...args) {
|
|
124
|
-
this.
|
|
162
|
+
this.proxy("mark", [message, ...args]);
|
|
125
163
|
}
|
|
126
164
|
debug(message, ...args) {
|
|
127
|
-
this.
|
|
165
|
+
this.proxy("debug", [message, ...args]);
|
|
128
166
|
}
|
|
129
167
|
};
|
|
130
168
|
const logger = new CustomLogger("default");
|
|
@@ -499,6 +537,11 @@ const BilibiliValidateCaptchaParamsSchema = zod.default.object({
|
|
|
499
537
|
validate: zod.default.string({ error: "验证码validate必须是字符串" }).min(1, { error: "验证码validate不能为空" }),
|
|
500
538
|
seccode: zod.default.string({ error: "验证码seccode必须是字符串" }).min(1, { error: "验证码seccode不能为空" })
|
|
501
539
|
});
|
|
540
|
+
const BilibiliDanmakuParamsSchema = zod.default.object({
|
|
541
|
+
methodType: zod.default.literal("实时弹幕", { error: "方法类型必须是\"实时弹幕\"" }),
|
|
542
|
+
cid: smartNumber("CID不能为空", 1, true),
|
|
543
|
+
segment_index: zod.default.coerce.number({ error: "分段序号必须是数字" }).int({ error: "分段序号必须是整数" }).positive({ error: "分段序号必须是正数" }).default(1).optional()
|
|
544
|
+
});
|
|
502
545
|
const BilibiliValidationSchemas = {
|
|
503
546
|
单个视频作品数据: BilibiliVideoParamsSchema,
|
|
504
547
|
单个视频下载信息数据: BilibiliVideoDownloadParamsSchema,
|
|
@@ -525,7 +568,8 @@ const BilibiliValidationSchemas = {
|
|
|
525
568
|
专栏文章基本信息: BilibiliArticleInfoParamsSchema,
|
|
526
569
|
文集基本信息: BilibiliColumnInfoParamsSchema,
|
|
527
570
|
从_v_voucher_申请_captcha: BilibiliApplyCaptchaParamsSchema,
|
|
528
|
-
验证验证码结果: BilibiliValidateCaptchaParamsSchema
|
|
571
|
+
验证验证码结果: BilibiliValidateCaptchaParamsSchema,
|
|
572
|
+
实时弹幕: BilibiliDanmakuParamsSchema
|
|
529
573
|
};
|
|
530
574
|
const BilibiliMethodRoutes = {
|
|
531
575
|
单个视频作品数据: "/fetch_one_video",
|
|
@@ -553,7 +597,8 @@ const BilibiliMethodRoutes = {
|
|
|
553
597
|
专栏文章基本信息: "/fetch_article_info",
|
|
554
598
|
文集基本信息: "/fetch_column_info",
|
|
555
599
|
从_v_voucher_申请_captcha: "/apply_captcha",
|
|
556
|
-
验证验证码结果: "/validate_captcha"
|
|
600
|
+
验证验证码结果: "/validate_captcha",
|
|
601
|
+
实时弹幕: "/fetch_danmaku"
|
|
557
602
|
};
|
|
558
603
|
|
|
559
604
|
//#endregion
|
|
@@ -723,51 +768,26 @@ var xiaohongshuSign = class {
|
|
|
723
768
|
return this.client.signXsPost(path$1, a1Cookie, clientType, body);
|
|
724
769
|
}
|
|
725
770
|
/**
|
|
726
|
-
* 生成X-S签名(兼容旧接口)
|
|
727
|
-
* @param url - 请求URL
|
|
728
|
-
* @param body - 请求体
|
|
729
|
-
* @param userAgent - User-Agent(暂未使用)
|
|
730
|
-
* @param method - 请求方法,默认为 'POST'
|
|
731
|
-
* @param a1Cookie - a1 cookie值
|
|
732
|
-
* @returns X-S签名
|
|
733
|
-
*/
|
|
734
|
-
static generateXS(url$1, body, userAgent, method = "POST", a1Cookie = "") {
|
|
735
|
-
try {
|
|
736
|
-
const urlObj = new URL(url$1);
|
|
737
|
-
const path$1 = urlObj.pathname + urlObj.search;
|
|
738
|
-
if (method.toUpperCase() === "GET") {
|
|
739
|
-
const params = typeof body === "object" ? body : {};
|
|
740
|
-
return this.generateXSGet(path$1, a1Cookie, "xhs-pc-web", params);
|
|
741
|
-
} else {
|
|
742
|
-
const requestBody = typeof body === "object" ? body : {};
|
|
743
|
-
return this.generateXSPost(path$1, a1Cookie, "xhs-pc-web", requestBody);
|
|
744
|
-
}
|
|
745
|
-
} catch (error) {
|
|
746
|
-
console.error("生成X-S签名失败:", error);
|
|
747
|
-
throw new Error(`签名生成失败: ${error}`);
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
/**
|
|
751
771
|
* 生成X-S-Common参数
|
|
752
|
-
* @param
|
|
772
|
+
* @param cookies - cookie字符串
|
|
753
773
|
* @returns Base64编码的随机字符串
|
|
754
774
|
*/
|
|
755
|
-
static generateXSCommon(
|
|
756
|
-
return
|
|
775
|
+
static generateXSCommon(cookies) {
|
|
776
|
+
return this.client.signXsCommon(cookies);
|
|
757
777
|
}
|
|
758
778
|
/**
|
|
759
779
|
* 生成X-T时间戳
|
|
760
780
|
* @returns 当前时间戳字符串
|
|
761
781
|
*/
|
|
762
782
|
static generateXT() {
|
|
763
|
-
return
|
|
783
|
+
return this.client.getXT();
|
|
764
784
|
}
|
|
765
785
|
/**
|
|
766
786
|
* 生成X-B3-Traceid
|
|
767
787
|
* @returns 16位随机字符串
|
|
768
788
|
*/
|
|
769
789
|
static generateXB3Traceid() {
|
|
770
|
-
return
|
|
790
|
+
return this.client.getB3TraceId();
|
|
771
791
|
}
|
|
772
792
|
/**
|
|
773
793
|
* 从cookie字符串中提取a1值
|
|
@@ -1153,7 +1173,7 @@ const isRecoverableError = (error) => {
|
|
|
1153
1173
|
* 延迟函数
|
|
1154
1174
|
* @param ms - 延迟毫秒数
|
|
1155
1175
|
*/
|
|
1156
|
-
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1176
|
+
const delay = (ms) => new Promise((resolve$1) => setTimeout(resolve$1, ms));
|
|
1157
1177
|
/**
|
|
1158
1178
|
* 创建网络错误响应
|
|
1159
1179
|
* @param error - 错误对象
|
|
@@ -1253,7 +1273,8 @@ const fetchResponse = async (config, maxRetries = DEFAULT_MAX_RETRIES) => {
|
|
|
1253
1273
|
* 判断结果是否为网络错误响应
|
|
1254
1274
|
* @param result - 请求结果
|
|
1255
1275
|
* @returns 是否为ErrorResult
|
|
1256
|
-
*
|
|
1276
|
+
*
|
|
1277
|
+
* 通过检查 error 字段中的 amagiError 来区分网络错误和业务错误
|
|
1257
1278
|
*/
|
|
1258
1279
|
const isNetworkErrorResult = (result) => {
|
|
1259
1280
|
if (result === null || typeof result !== "object") return false;
|
|
@@ -1522,6 +1543,17 @@ var BiLiBiLiAPI = class {
|
|
|
1522
1543
|
}
|
|
1523
1544
|
};
|
|
1524
1545
|
}
|
|
1546
|
+
/**
|
|
1547
|
+
* 获取实时弹幕(web端 protobuf 接口)
|
|
1548
|
+
* @see https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/danmaku/danmaku_proto.md
|
|
1549
|
+
*/
|
|
1550
|
+
实时弹幕(data$1) {
|
|
1551
|
+
return `https://api.bilibili.com/x/v2/dm/web/seg.so?${new URLSearchParams({
|
|
1552
|
+
type: "1",
|
|
1553
|
+
oid: data$1.cid.toString(),
|
|
1554
|
+
segment_index: (data$1.segment_index ?? 1).toString()
|
|
1555
|
+
}).toString()}`;
|
|
1556
|
+
}
|
|
1525
1557
|
};
|
|
1526
1558
|
/** 该类下的所有方法只会返回拼接好参数后的 Url 地址,需要手动请求该地址以获取数据 */
|
|
1527
1559
|
const bilibiliApiUrls = new BiLiBiLiAPI();
|
|
@@ -1586,7 +1618,8 @@ const bilibili = {
|
|
|
1586
1618
|
getColumnInfo: createBilibiliApiMethod("文集基本信息"),
|
|
1587
1619
|
getUserProfileDetail: createBilibiliApiMethod("用户空间详细信息"),
|
|
1588
1620
|
applyVoucherCaptcha: createBilibiliApiMethod("从_v_voucher_申请_captcha"),
|
|
1589
|
-
validateCaptcha: createBilibiliApiMethod("验证验证码结果")
|
|
1621
|
+
validateCaptcha: createBilibiliApiMethod("验证验证码结果"),
|
|
1622
|
+
getDanmaku: createBilibiliApiMethod("实时弹幕")
|
|
1590
1623
|
};
|
|
1591
1624
|
/**
|
|
1592
1625
|
* 创建绑定了cookie的B站API对象
|
|
@@ -1620,7 +1653,8 @@ const createBoundBilibiliApi = (cookie, requestConfig) => {
|
|
|
1620
1653
|
getColumnInfo: createBoundBilibiliApiMethod("文集基本信息", cookie),
|
|
1621
1654
|
getUserProfileDetail: createBoundBilibiliApiMethod("用户空间详细信息", cookie),
|
|
1622
1655
|
applyVoucherCaptcha: createBoundBilibiliApiMethod("从_v_voucher_申请_captcha", cookie),
|
|
1623
|
-
validateCaptcha: createBoundBilibiliApiMethod("验证验证码结果", cookie)
|
|
1656
|
+
validateCaptcha: createBoundBilibiliApiMethod("验证验证码结果", cookie),
|
|
1657
|
+
getDanmaku: createBoundBilibiliApiMethod("实时弹幕", cookie)
|
|
1624
1658
|
};
|
|
1625
1659
|
};
|
|
1626
1660
|
|
|
@@ -1676,6 +1710,101 @@ const bv2av = (bvid) => {
|
|
|
1676
1710
|
return Number(tmp & MASK_CODE ^ XOR_CODE);
|
|
1677
1711
|
};
|
|
1678
1712
|
|
|
1713
|
+
//#endregion
|
|
1714
|
+
//#region src/platform/bilibili/sign/danmaku_proto.ts
|
|
1715
|
+
/**
|
|
1716
|
+
* B站弹幕 Protobuf 定义(内联)
|
|
1717
|
+
* @see https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/danmaku/danmaku_proto.md
|
|
1718
|
+
*/
|
|
1719
|
+
const DANMAKU_PROTO_JSON = { nested: { bilibili: { nested: { community: { nested: { service: { nested: { dm: { nested: { v1: { nested: {
|
|
1720
|
+
DmSegMobileReply: { fields: { elems: {
|
|
1721
|
+
rule: "repeated",
|
|
1722
|
+
type: "DanmakuElem",
|
|
1723
|
+
id: 1
|
|
1724
|
+
} } },
|
|
1725
|
+
DanmakuElem: { fields: {
|
|
1726
|
+
id: {
|
|
1727
|
+
type: "int64",
|
|
1728
|
+
id: 1
|
|
1729
|
+
},
|
|
1730
|
+
progress: {
|
|
1731
|
+
type: "int32",
|
|
1732
|
+
id: 2
|
|
1733
|
+
},
|
|
1734
|
+
mode: {
|
|
1735
|
+
type: "int32",
|
|
1736
|
+
id: 3
|
|
1737
|
+
},
|
|
1738
|
+
fontsize: {
|
|
1739
|
+
type: "int32",
|
|
1740
|
+
id: 4
|
|
1741
|
+
},
|
|
1742
|
+
color: {
|
|
1743
|
+
type: "uint32",
|
|
1744
|
+
id: 5
|
|
1745
|
+
},
|
|
1746
|
+
midHash: {
|
|
1747
|
+
type: "string",
|
|
1748
|
+
id: 6
|
|
1749
|
+
},
|
|
1750
|
+
content: {
|
|
1751
|
+
type: "string",
|
|
1752
|
+
id: 7
|
|
1753
|
+
},
|
|
1754
|
+
ctime: {
|
|
1755
|
+
type: "int64",
|
|
1756
|
+
id: 8
|
|
1757
|
+
},
|
|
1758
|
+
weight: {
|
|
1759
|
+
type: "int32",
|
|
1760
|
+
id: 9
|
|
1761
|
+
},
|
|
1762
|
+
action: {
|
|
1763
|
+
type: "string",
|
|
1764
|
+
id: 10
|
|
1765
|
+
},
|
|
1766
|
+
pool: {
|
|
1767
|
+
type: "int32",
|
|
1768
|
+
id: 11
|
|
1769
|
+
},
|
|
1770
|
+
idStr: {
|
|
1771
|
+
type: "string",
|
|
1772
|
+
id: 12
|
|
1773
|
+
},
|
|
1774
|
+
attr: {
|
|
1775
|
+
type: "int32",
|
|
1776
|
+
id: 13
|
|
1777
|
+
},
|
|
1778
|
+
animation: {
|
|
1779
|
+
type: "string",
|
|
1780
|
+
id: 22
|
|
1781
|
+
}
|
|
1782
|
+
} }
|
|
1783
|
+
} } } } } } } } } } } };
|
|
1784
|
+
let DmSegMobileReplyType = null;
|
|
1785
|
+
/**
|
|
1786
|
+
* 获取 protobuf 类型定义
|
|
1787
|
+
*/
|
|
1788
|
+
function getProtoType() {
|
|
1789
|
+
if (DmSegMobileReplyType) return DmSegMobileReplyType;
|
|
1790
|
+
DmSegMobileReplyType = protobufjs.default.Root.fromJSON(DANMAKU_PROTO_JSON).lookupType("bilibili.community.service.dm.v1.DmSegMobileReply");
|
|
1791
|
+
return DmSegMobileReplyType;
|
|
1792
|
+
}
|
|
1793
|
+
/**
|
|
1794
|
+
* 解析弹幕分段响应
|
|
1795
|
+
* @param data - 二进制 protobuf 数据
|
|
1796
|
+
* @returns 解析后的弹幕数据
|
|
1797
|
+
*/
|
|
1798
|
+
function parseDmSegMobileReply(data$1) {
|
|
1799
|
+
const messageType = getProtoType();
|
|
1800
|
+
const buffer = data$1 instanceof Uint8Array ? data$1 : new Uint8Array(data$1);
|
|
1801
|
+
const message = messageType.decode(buffer);
|
|
1802
|
+
return messageType.toObject(message, {
|
|
1803
|
+
longs: String,
|
|
1804
|
+
defaults: true
|
|
1805
|
+
});
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1679
1808
|
//#endregion
|
|
1680
1809
|
//#region src/platform/bilibili/sign/wbi.ts
|
|
1681
1810
|
/**
|
|
@@ -1975,6 +2104,7 @@ const bilibiliUtils = {
|
|
|
1975
2104
|
av2bv,
|
|
1976
2105
|
bv2av
|
|
1977
2106
|
},
|
|
2107
|
+
danmaku: { parseDmSegMobileReply },
|
|
1978
2108
|
bilibiliApiUrls,
|
|
1979
2109
|
api: bilibili
|
|
1980
2110
|
};
|
|
@@ -2314,6 +2444,48 @@ const fetchBilibili = async (data$1, cookie, requestConfig) => {
|
|
|
2314
2444
|
url: bilibiliApiUrls.验证验证码结果(data$1).Url,
|
|
2315
2445
|
data: bilibiliApiUrls.验证验证码结果(data$1).Body
|
|
2316
2446
|
});
|
|
2447
|
+
case "实时弹幕": {
|
|
2448
|
+
const url$1 = bilibiliApiUrls.实时弹幕({
|
|
2449
|
+
cid: data$1.cid,
|
|
2450
|
+
segment_index: data$1.segment_index
|
|
2451
|
+
});
|
|
2452
|
+
try {
|
|
2453
|
+
const response = await fetchData({
|
|
2454
|
+
...baseRequestConfig,
|
|
2455
|
+
url: url$1,
|
|
2456
|
+
responseType: "arraybuffer"
|
|
2457
|
+
});
|
|
2458
|
+
if (isNetworkErrorResult(response)) {
|
|
2459
|
+
const networkError = new Error(response.error.amagiError.errorDescription);
|
|
2460
|
+
Object.assign(networkError, {
|
|
2461
|
+
code: response.error.code,
|
|
2462
|
+
data: null,
|
|
2463
|
+
amagiError: {
|
|
2464
|
+
...response.error.amagiError,
|
|
2465
|
+
requestType: data$1.methodType
|
|
2466
|
+
}
|
|
2467
|
+
});
|
|
2468
|
+
throw networkError;
|
|
2469
|
+
}
|
|
2470
|
+
return {
|
|
2471
|
+
code: 0,
|
|
2472
|
+
message: "success",
|
|
2473
|
+
data: { elems: parseDmSegMobileReply(response).elems }
|
|
2474
|
+
};
|
|
2475
|
+
} catch (error) {
|
|
2476
|
+
if (error && typeof error === "object" && "code" in error) return error;
|
|
2477
|
+
const Err = {
|
|
2478
|
+
errorDescription: `获取弹幕数据失败:${error instanceof Error ? error.message : "未知错误"}`,
|
|
2479
|
+
requestType: data$1.methodType,
|
|
2480
|
+
requestUrl: url$1
|
|
2481
|
+
};
|
|
2482
|
+
return {
|
|
2483
|
+
code: amagiAPIErrorCode.UNKNOWN,
|
|
2484
|
+
data: null,
|
|
2485
|
+
amagiError: Err
|
|
2486
|
+
};
|
|
2487
|
+
}
|
|
2488
|
+
}
|
|
2317
2489
|
default:
|
|
2318
2490
|
logger.warn(`未知的B站数据接口:「${logger.red(data$1.methodType)}」`);
|
|
2319
2491
|
return null;
|
|
@@ -3659,11 +3831,53 @@ const DouyinData = async (data$1, cookie, requestConfig) => {
|
|
|
3659
3831
|
}
|
|
3660
3832
|
case "评论数据": {
|
|
3661
3833
|
const urlGenerator = (params) => douyinApiUrls$1.评论(params);
|
|
3662
|
-
return await fetchPaginatedData(
|
|
3834
|
+
return await fetchPaginatedData({
|
|
3835
|
+
type: data$1.methodType,
|
|
3836
|
+
apiUrlGenerator: urlGenerator,
|
|
3837
|
+
params: {
|
|
3838
|
+
...data$1,
|
|
3839
|
+
cursor: data$1.cursor ?? 0
|
|
3840
|
+
},
|
|
3841
|
+
maxPageSize: 50,
|
|
3842
|
+
requestConfig: baseRequestConfig,
|
|
3843
|
+
signType,
|
|
3844
|
+
extractList: (resp) => resp.comments ?? [],
|
|
3845
|
+
updateParams: (params, resp) => ({
|
|
3846
|
+
...params,
|
|
3847
|
+
cursor: resp.cursor
|
|
3848
|
+
}),
|
|
3849
|
+
hasMore: (resp) => resp.has_more === 1,
|
|
3850
|
+
formatFinalResponse: (resp, list) => ({
|
|
3851
|
+
...resp,
|
|
3852
|
+
comments: list,
|
|
3853
|
+
cursor: resp.cursor ?? list.length
|
|
3854
|
+
})
|
|
3855
|
+
});
|
|
3663
3856
|
}
|
|
3664
3857
|
case "指定评论回复数据": {
|
|
3665
3858
|
const urlGenerator = (params) => douyinApiUrls$1.二级评论(params);
|
|
3666
|
-
return await fetchPaginatedData(
|
|
3859
|
+
return await fetchPaginatedData({
|
|
3860
|
+
type: data$1.methodType,
|
|
3861
|
+
apiUrlGenerator: urlGenerator,
|
|
3862
|
+
params: {
|
|
3863
|
+
...data$1,
|
|
3864
|
+
cursor: data$1.cursor ?? 0
|
|
3865
|
+
},
|
|
3866
|
+
maxPageSize: 3,
|
|
3867
|
+
requestConfig: baseRequestConfig,
|
|
3868
|
+
signType: "x_bogus",
|
|
3869
|
+
extractList: (resp) => resp.comments ?? [],
|
|
3870
|
+
updateParams: (params, resp) => ({
|
|
3871
|
+
...params,
|
|
3872
|
+
cursor: resp.cursor
|
|
3873
|
+
}),
|
|
3874
|
+
hasMore: (resp) => resp.has_more === 1,
|
|
3875
|
+
formatFinalResponse: (resp, list) => ({
|
|
3876
|
+
...resp,
|
|
3877
|
+
comments: list,
|
|
3878
|
+
cursor: resp.cursor ?? list.length
|
|
3879
|
+
})
|
|
3880
|
+
});
|
|
3667
3881
|
}
|
|
3668
3882
|
case "用户主页数据": {
|
|
3669
3883
|
const url$1 = douyinApiUrls$1.用户主页信息({ sec_uid: data$1.sec_uid });
|
|
@@ -3715,10 +3929,6 @@ const DouyinData = async (data$1, cookie, requestConfig) => {
|
|
|
3715
3929
|
});
|
|
3716
3930
|
}
|
|
3717
3931
|
case "搜索数据": {
|
|
3718
|
-
let search_id = "";
|
|
3719
|
-
const maxPageSize = 15;
|
|
3720
|
-
let fetchedSearchList = [];
|
|
3721
|
-
let tmpresp = null;
|
|
3722
3932
|
const searchType = data$1.type ?? "综合";
|
|
3723
3933
|
const refererUrl = searchType === "用户" ? `https://www.douyin.com/search/${encodeURIComponent(String(data$1.query))}?type=user` : searchType === "视频" ? `https://www.douyin.com/search/${encodeURIComponent(String(data$1.query))}?type=video` : `https://www.douyin.com/root/search/${encodeURIComponent(String(data$1.query))}`;
|
|
3724
3934
|
const customConfig = {
|
|
@@ -3730,146 +3940,82 @@ const DouyinData = async (data$1, cookie, requestConfig) => {
|
|
|
3730
3940
|
};
|
|
3731
3941
|
const isUserSearch = searchType === "用户";
|
|
3732
3942
|
const isVideoSearch = searchType === "视频";
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3943
|
+
return await fetchPaginatedData({
|
|
3944
|
+
type: data$1.methodType,
|
|
3945
|
+
apiUrlGenerator: (params) => douyinApiUrls$1.搜索(params),
|
|
3946
|
+
params: {
|
|
3737
3947
|
query: data$1.query,
|
|
3738
3948
|
type: data$1.type,
|
|
3739
|
-
number:
|
|
3740
|
-
search_id:
|
|
3741
|
-
}
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
请求类型:「${data$1.methodType}」
|
|
3756
|
-
搜索关键词:「${data$1.query}」
|
|
3757
|
-
请求URL:${url$1}
|
|
3758
|
-
`;
|
|
3759
|
-
!isFirstRequest && logger.warn(warningMessage);
|
|
3760
|
-
return {
|
|
3761
|
-
code: douoyinAPIErrorCode.COOKIE,
|
|
3762
|
-
data: raw,
|
|
3763
|
-
amagiError: Err,
|
|
3764
|
-
amagiMessage: warningMessage
|
|
3765
|
-
};
|
|
3766
|
-
}
|
|
3767
|
-
const userList = raw.user_list;
|
|
3768
|
-
if (isFirstRequest && (!userList || userList.length === 0)) {
|
|
3769
|
-
const Err = {
|
|
3770
|
-
errorDescription: "抖音用户搜索接口第一次请求就返回空数组,可能该关键词无搜索结果或触发风控限制,你的抖音Cookie可能已经失效!",
|
|
3771
|
-
requestType: data$1.methodType ?? "未知请求类型",
|
|
3772
|
-
requestUrl: url$1
|
|
3773
|
-
};
|
|
3774
|
-
const warningMessage = `
|
|
3775
|
-
获取响应数据失败!原因:${logger.yellow("用户搜索接口第一次请求就返回空数组,你的抖音Cookie可能已经失效!")}
|
|
3776
|
-
请求类型:「${data$1.methodType}」
|
|
3777
|
-
搜索关键词:「${data$1.query}」
|
|
3778
|
-
请求URL:${url$1}
|
|
3779
|
-
`;
|
|
3780
|
-
logger.warn(warningMessage);
|
|
3781
|
-
return {
|
|
3782
|
-
data: raw,
|
|
3783
|
-
amagiError: Err,
|
|
3784
|
-
amagiMessage: warningMessage
|
|
3785
|
-
};
|
|
3786
|
-
}
|
|
3787
|
-
if (Array.isArray(userList) && userList.length > 0) fetchedSearchList.push(...userList);
|
|
3788
|
-
tmpresp = raw;
|
|
3789
|
-
search_id = raw.rid ?? search_id;
|
|
3790
|
-
if (typeof raw.has_more === "number" && raw.has_more === 0) break;
|
|
3791
|
-
if (!userList || userList.length === 0) break;
|
|
3792
|
-
} else if (isVideoSearch) {
|
|
3793
|
-
if (!raw || typeof raw !== "object") {
|
|
3794
|
-
const Err = {
|
|
3795
|
-
errorDescription: "抖音视频搜索接口返回无有效数据,疑似触发反爬机制,你的抖音Cookie可能已经失效,你的抖音Cookie可能已经失效!",
|
|
3796
|
-
requestType: data$1.methodType ?? "未知请求类型",
|
|
3797
|
-
requestUrl: url$1
|
|
3798
|
-
};
|
|
3799
|
-
const warningMessage = `
|
|
3800
|
-
获取响应数据失败!原因:${logger.yellow("视频搜索接口返回无有效数据,疑似触发反爬机制,你的抖音Cookie可能已经失效!")}
|
|
3801
|
-
请求类型:「${data$1.methodType}」
|
|
3802
|
-
搜索关键词:「${data$1.query}」
|
|
3803
|
-
请求URL:${url$1}
|
|
3804
|
-
`;
|
|
3805
|
-
!isFirstRequest && logger.warn(warningMessage);
|
|
3806
|
-
return {
|
|
3807
|
-
code: douoyinAPIErrorCode.COOKIE,
|
|
3808
|
-
data: raw,
|
|
3809
|
-
amagiError: Err,
|
|
3810
|
-
amagiMessage: warningMessage
|
|
3811
|
-
};
|
|
3812
|
-
}
|
|
3813
|
-
const videoList = raw.data;
|
|
3814
|
-
if (isFirstRequest && (!videoList || videoList.length === 0)) {
|
|
3815
|
-
const Err = {
|
|
3816
|
-
errorDescription: "抖音视频搜索接口第一次请求就返回空数组,可能该关键词无搜索结果或触发风控限制,你的抖音Cookie可能已经失效!",
|
|
3817
|
-
requestType: data$1.methodType ?? "未知请求类型",
|
|
3818
|
-
requestUrl: url$1
|
|
3819
|
-
};
|
|
3820
|
-
const warningMessage = `
|
|
3821
|
-
获取响应数据失败!原因:${logger.yellow("视频搜索接口第一次请求就返回空数组,你的抖音Cookie可能已经失效!")}
|
|
3822
|
-
请求类型:「${data$1.methodType}」
|
|
3823
|
-
搜索关键词:「${data$1.query}」
|
|
3824
|
-
请求URL:${url$1}
|
|
3825
|
-
`;
|
|
3826
|
-
logger.warn(warningMessage);
|
|
3949
|
+
number: data$1.number ?? 10,
|
|
3950
|
+
search_id: ""
|
|
3951
|
+
},
|
|
3952
|
+
maxPageSize: 15,
|
|
3953
|
+
requestConfig: customConfig,
|
|
3954
|
+
signType: null,
|
|
3955
|
+
processRawResponse: (raw) => {
|
|
3956
|
+
if (!isUserSearch && !isVideoSearch) {
|
|
3957
|
+
const responses = filterSearchResponses(typeof raw === "string" ? parseDouyinMultiJson(raw) : [raw]);
|
|
3958
|
+
if (responses.length === 0) return raw;
|
|
3959
|
+
const mergedData = [];
|
|
3960
|
+
let lastValid = {};
|
|
3961
|
+
for (const resp of responses) {
|
|
3962
|
+
if (Array.isArray(resp.data) && resp.data.length > 0) mergedData.push(...resp.data);
|
|
3963
|
+
lastValid = resp;
|
|
3964
|
+
}
|
|
3827
3965
|
return {
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
amagiMessage: warningMessage
|
|
3966
|
+
...lastValid,
|
|
3967
|
+
data: mergedData
|
|
3831
3968
|
};
|
|
3832
3969
|
}
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
if (
|
|
3837
|
-
|
|
3838
|
-
}
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3970
|
+
return raw;
|
|
3971
|
+
},
|
|
3972
|
+
extractList: (resp) => {
|
|
3973
|
+
if (isUserSearch) return resp.user_list ?? [];
|
|
3974
|
+
return resp.data ?? [];
|
|
3975
|
+
},
|
|
3976
|
+
updateParams: (params, resp) => {
|
|
3977
|
+
let nextSearchId = params.search_id;
|
|
3978
|
+
if (isUserSearch) nextSearchId = resp.rid ?? nextSearchId;
|
|
3979
|
+
else nextSearchId = resp.log_pb?.impr_id ?? nextSearchId;
|
|
3980
|
+
return {
|
|
3981
|
+
...params,
|
|
3982
|
+
search_id: nextSearchId
|
|
3983
|
+
};
|
|
3984
|
+
},
|
|
3985
|
+
hasMore: (resp) => {
|
|
3986
|
+
return resp.has_more !== 0;
|
|
3987
|
+
},
|
|
3988
|
+
validateFirstPage: (list, raw, url$1) => {
|
|
3989
|
+
const typeStr = isUserSearch ? "用户" : isVideoSearch ? "视频" : "综合";
|
|
3990
|
+
let isInvalidResponse = false;
|
|
3991
|
+
const rawAny = raw;
|
|
3992
|
+
if (!rawAny || typeof rawAny !== "object") isInvalidResponse = true;
|
|
3993
|
+
else if (isUserSearch && !rawAny.user_list) isInvalidResponse = true;
|
|
3994
|
+
else if (isVideoSearch && !rawAny.data) isInvalidResponse = true;
|
|
3995
|
+
else if (!isUserSearch && !isVideoSearch && !rawAny.data) isInvalidResponse = true;
|
|
3996
|
+
if (isInvalidResponse) {
|
|
3997
|
+
const desc = `抖音${typeStr}搜索返回无有效数据,疑似触发反爬机制,你的抖音Cookie可能已经失效!`;
|
|
3846
3998
|
const warningMessage = `
|
|
3847
|
-
获取响应数据失败!原因:${logger.yellow(
|
|
3999
|
+
获取响应数据失败!原因:${logger.yellow(`${typeStr}搜索返回无有效数据,疑似触发反爬机制`)}
|
|
3848
4000
|
请求类型:「${data$1.methodType}」
|
|
3849
4001
|
搜索关键词:「${data$1.query}」
|
|
3850
4002
|
请求URL:${url$1}
|
|
3851
4003
|
`;
|
|
3852
|
-
!isFirstRequest && logger.warn(warningMessage);
|
|
3853
4004
|
return {
|
|
3854
4005
|
code: douoyinAPIErrorCode.COOKIE,
|
|
3855
4006
|
data: raw,
|
|
3856
|
-
amagiError:
|
|
4007
|
+
amagiError: {
|
|
4008
|
+
errorDescription: desc,
|
|
4009
|
+
requestType: data$1.methodType ?? "未知请求类型",
|
|
4010
|
+
requestUrl: url$1
|
|
4011
|
+
},
|
|
3857
4012
|
amagiMessage: warningMessage
|
|
3858
4013
|
};
|
|
3859
4014
|
}
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
tmpresp = resp;
|
|
3863
|
-
search_id = resp.log_pb?.impr_id ?? search_id;
|
|
3864
|
-
}
|
|
3865
|
-
if (isFirstRequest && fetchedSearchList.length === 0) {
|
|
3866
|
-
const Err = {
|
|
3867
|
-
errorDescription: "抖音综合搜索接口第一次请求就返回空数组,可能该关键词无搜索结果或触发风控限制,你的抖音Cookie可能已经失效!",
|
|
3868
|
-
requestType: data$1.methodType ?? "未知请求类型",
|
|
3869
|
-
requestUrl: url$1
|
|
3870
|
-
};
|
|
4015
|
+
if (!list || list.length === 0) {
|
|
4016
|
+
const desc = `抖音${typeStr}搜索接口第一次请求就返回空数组,可能该关键词无搜索结果或触发风控限制,你的抖音Cookie可能已经失效!`;
|
|
3871
4017
|
const warningMessage = `
|
|
3872
|
-
获取响应数据失败!原因:${logger.yellow(
|
|
4018
|
+
获取响应数据失败!原因:${logger.yellow(`${typeStr}搜索接口第一次请求就返回空数组,你的抖音Cookie可能已经失效!`)}
|
|
3873
4019
|
请求类型:「${data$1.methodType}」
|
|
3874
4020
|
搜索关键词:「${data$1.query}」
|
|
3875
4021
|
请求URL:${url$1}
|
|
@@ -3877,25 +4023,27 @@ const DouyinData = async (data$1, cookie, requestConfig) => {
|
|
|
3877
4023
|
logger.warn(warningMessage);
|
|
3878
4024
|
return {
|
|
3879
4025
|
data: raw,
|
|
3880
|
-
amagiError:
|
|
4026
|
+
amagiError: {
|
|
4027
|
+
errorDescription: desc,
|
|
4028
|
+
requestType: data$1.methodType ?? "未知请求类型",
|
|
4029
|
+
requestUrl: url$1
|
|
4030
|
+
},
|
|
3881
4031
|
amagiMessage: warningMessage
|
|
3882
4032
|
};
|
|
3883
4033
|
}
|
|
3884
|
-
|
|
4034
|
+
return null;
|
|
4035
|
+
},
|
|
4036
|
+
formatFinalResponse: (resp, list) => {
|
|
4037
|
+
if (isUserSearch) return {
|
|
4038
|
+
...resp,
|
|
4039
|
+
user_list: list
|
|
4040
|
+
};
|
|
4041
|
+
return {
|
|
4042
|
+
...resp,
|
|
4043
|
+
data: list
|
|
4044
|
+
};
|
|
3885
4045
|
}
|
|
3886
|
-
|
|
3887
|
-
}
|
|
3888
|
-
const slicedList = Number(data$1.number ?? 10) === 0 ? [] : fetchedSearchList.slice(0, Number(data$1.number ?? 10));
|
|
3889
|
-
return isUserSearch ? {
|
|
3890
|
-
...tmpresp ?? {},
|
|
3891
|
-
user_list: slicedList
|
|
3892
|
-
} : isVideoSearch ? {
|
|
3893
|
-
...tmpresp ?? {},
|
|
3894
|
-
data: slicedList
|
|
3895
|
-
} : {
|
|
3896
|
-
...tmpresp ?? {},
|
|
3897
|
-
data: slicedList
|
|
3898
|
-
};
|
|
4046
|
+
});
|
|
3899
4047
|
}
|
|
3900
4048
|
case "动态表情数据": {
|
|
3901
4049
|
const url$1 = douyinApiUrls$1.互动表情();
|
|
@@ -4031,40 +4179,48 @@ const DouyinData = async (data$1, cookie, requestConfig) => {
|
|
|
4031
4179
|
};
|
|
4032
4180
|
/**
|
|
4033
4181
|
* 通用的分页请求函数
|
|
4034
|
-
*
|
|
4035
|
-
*
|
|
4036
|
-
* @
|
|
4037
|
-
* @
|
|
4038
|
-
* @
|
|
4039
|
-
* @
|
|
4040
|
-
* @
|
|
4182
|
+
* 封装了循环分页请求、数据合并、错误处理和反爬检测逻辑
|
|
4183
|
+
*
|
|
4184
|
+
* @template T - 列表项类型
|
|
4185
|
+
* @template P - 参数类型
|
|
4186
|
+
* @template R - 返回值类型
|
|
4187
|
+
* @template RawResp - 原始响应类型
|
|
4188
|
+
* @param config - 分页请求配置对象
|
|
4189
|
+
* @returns Promise<R> 返回最终构造的数据对象
|
|
4041
4190
|
*/
|
|
4042
|
-
const fetchPaginatedData = async (
|
|
4043
|
-
|
|
4044
|
-
let
|
|
4045
|
-
|
|
4191
|
+
const fetchPaginatedData = async (config) => {
|
|
4192
|
+
const { type, apiUrlGenerator, params, maxPageSize, requestConfig, signType = "a_bogus", extractList, updateParams, hasMore, formatFinalResponse, processRawResponse, validateFirstPage } = config;
|
|
4193
|
+
let currentParams = { ...params };
|
|
4194
|
+
const fetchedData = [];
|
|
4195
|
+
let lastResponse = {};
|
|
4196
|
+
let isFirstRequest = true;
|
|
4046
4197
|
const userAgent = requestConfig.headers?.["User-Agent"];
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
const
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
const response = await GlobalGetData$2(type, {
|
|
4198
|
+
const targetNumber = Number(params.number ?? maxPageSize);
|
|
4199
|
+
while (fetchedData.length < targetNumber) {
|
|
4200
|
+
const remaining = targetNumber - fetchedData.length;
|
|
4201
|
+
currentParams.number = Math.min(remaining, maxPageSize);
|
|
4202
|
+
const url$1 = apiUrlGenerator(currentParams);
|
|
4203
|
+
const finalUrl = signType ? buildSignedUrl(url$1, signType, userAgent) : url$1;
|
|
4204
|
+
const raw = await GlobalGetData$2(type, {
|
|
4055
4205
|
...requestConfig,
|
|
4056
|
-
url:
|
|
4206
|
+
url: finalUrl
|
|
4057
4207
|
});
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4208
|
+
const response = processRawResponse ? processRawResponse(raw) : raw;
|
|
4209
|
+
if (response && response.amagiError) return response;
|
|
4210
|
+
const list = extractList(response);
|
|
4211
|
+
if (isFirstRequest && validateFirstPage) {
|
|
4212
|
+
const error = validateFirstPage(list, response, finalUrl);
|
|
4213
|
+
if (error) return error;
|
|
4214
|
+
}
|
|
4215
|
+
if (Array.isArray(list) && list.length > 0) fetchedData.push(...list);
|
|
4216
|
+
lastResponse = response;
|
|
4217
|
+
if (!hasMore(response)) break;
|
|
4218
|
+
if (!list || list.length === 0) break;
|
|
4219
|
+
currentParams = updateParams(currentParams, response);
|
|
4220
|
+
isFirstRequest = false;
|
|
4062
4221
|
}
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
comments: params.number === 0 ? [] : fetchedData.slice(0, Number(params.number ?? maxPageSize)),
|
|
4066
|
-
cursor: params.number === 0 ? 0 : fetchedData.length
|
|
4067
|
-
};
|
|
4222
|
+
const slicedData = targetNumber === 0 ? [] : fetchedData.slice(0, targetNumber);
|
|
4223
|
+
return formatFinalResponse(lastResponse, slicedData);
|
|
4068
4224
|
};
|
|
4069
4225
|
/**
|
|
4070
4226
|
* 全局数据获取函数
|
|
@@ -4373,7 +4529,7 @@ const XiaohongshuData = async (data$1, cookie, requestConfig) => {
|
|
|
4373
4529
|
headers: {
|
|
4374
4530
|
...baseRequestConfig.headers,
|
|
4375
4531
|
"x-s": xiaohongshuSign.generateXSPost(xiaohongshuApiUrls$1.首页推荐数据(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web", xiaohongshuApiUrls$1.首页推荐数据(data$1).Body),
|
|
4376
|
-
"x-s-common": xiaohongshuSign.generateXSCommon(),
|
|
4532
|
+
"x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
|
|
4377
4533
|
"x-t": xiaohongshuSign.generateXT()
|
|
4378
4534
|
}
|
|
4379
4535
|
});
|
|
@@ -4384,7 +4540,7 @@ const XiaohongshuData = async (data$1, cookie, requestConfig) => {
|
|
|
4384
4540
|
headers: {
|
|
4385
4541
|
...baseRequestConfig.headers,
|
|
4386
4542
|
"x-s": xiaohongshuSign.generateXSPost(xiaohongshuApiUrls$1.单个笔记数据(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web", xiaohongshuApiUrls$1.单个笔记数据(data$1).Body),
|
|
4387
|
-
"x-s-common": xiaohongshuSign.generateXSCommon(),
|
|
4543
|
+
"x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
|
|
4388
4544
|
"x-t": xiaohongshuSign.generateXT()
|
|
4389
4545
|
}
|
|
4390
4546
|
});
|
|
@@ -4404,7 +4560,7 @@ const XiaohongshuData = async (data$1, cookie, requestConfig) => {
|
|
|
4404
4560
|
headers: {
|
|
4405
4561
|
...baseRequestConfig$1.headers,
|
|
4406
4562
|
"x-s": xiaohongshuSign.generateXSGet(xiaohongshuApiUrls$1.评论数据(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web"),
|
|
4407
|
-
"x-s-common": xiaohongshuSign.generateXSCommon(),
|
|
4563
|
+
"x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
|
|
4408
4564
|
"x-t": xiaohongshuSign.generateXT()
|
|
4409
4565
|
}
|
|
4410
4566
|
});
|
|
@@ -4427,7 +4583,7 @@ const XiaohongshuData = async (data$1, cookie, requestConfig) => {
|
|
|
4427
4583
|
headers: {
|
|
4428
4584
|
...baseRequestConfig$1.headers,
|
|
4429
4585
|
"x-s": xiaohongshuSign.generateXSGet(xiaohongshuApiUrls$1.用户数据(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web"),
|
|
4430
|
-
"x-s-common": xiaohongshuSign.generateXSCommon(),
|
|
4586
|
+
"x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
|
|
4431
4587
|
"x-t": xiaohongshuSign.generateXT()
|
|
4432
4588
|
}
|
|
4433
4589
|
})),
|
|
@@ -4442,7 +4598,7 @@ const XiaohongshuData = async (data$1, cookie, requestConfig) => {
|
|
|
4442
4598
|
...baseRequestConfig.headers,
|
|
4443
4599
|
"x-b3-traceid": xiaohongshuSign.generateXB3Traceid(),
|
|
4444
4600
|
"x-s": xiaohongshuSign.generateXSGet(xiaohongshuApiUrls$1.用户笔记数据(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web"),
|
|
4445
|
-
"x-s-common": xiaohongshuSign.generateXSCommon(),
|
|
4601
|
+
"x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
|
|
4446
4602
|
"x-t": xiaohongshuSign.generateXT()
|
|
4447
4603
|
}
|
|
4448
4604
|
});
|
|
@@ -4462,7 +4618,7 @@ const XiaohongshuData = async (data$1, cookie, requestConfig) => {
|
|
|
4462
4618
|
headers: {
|
|
4463
4619
|
...baseRequestConfig$1.headers,
|
|
4464
4620
|
"x-s": xiaohongshuSign.generateXSGet(xiaohongshuApiUrls$1.表情列表(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web"),
|
|
4465
|
-
"x-s-common": xiaohongshuSign.generateXSCommon(),
|
|
4621
|
+
"x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
|
|
4466
4622
|
"x-t": xiaohongshuSign.generateXT()
|
|
4467
4623
|
}
|
|
4468
4624
|
});
|
|
@@ -4474,7 +4630,7 @@ const XiaohongshuData = async (data$1, cookie, requestConfig) => {
|
|
|
4474
4630
|
headers: {
|
|
4475
4631
|
...baseRequestConfig.headers,
|
|
4476
4632
|
"x-s": xiaohongshuSign.generateXSPost(xiaohongshuApiUrls$1.搜索笔记(data$1).apiPath, xiaohongshuSign.extractA1FromCookie(cookie ?? ""), "xhs-pc-web"),
|
|
4477
|
-
"x-s-common": xiaohongshuSign.generateXSCommon(),
|
|
4633
|
+
"x-s-common": xiaohongshuSign.generateXSCommon(cookie ?? ""),
|
|
4478
4634
|
"x-t": xiaohongshuSign.generateXT()
|
|
4479
4635
|
}
|
|
4480
4636
|
});
|
|
@@ -4966,6 +5122,7 @@ const createAmagiClient = (options) => {
|
|
|
4966
5122
|
* @returns Express应用实例
|
|
4967
5123
|
*/
|
|
4968
5124
|
const startServer = (port = 4567) => {
|
|
5125
|
+
initLogger();
|
|
4969
5126
|
const app = (0, express.default)();
|
|
4970
5127
|
app.use(express.default.json());
|
|
4971
5128
|
app.use(express.default.urlencoded({ extended: true }));
|
|
@@ -5183,7 +5340,14 @@ let DynamicType = /* @__PURE__ */ function(DynamicType$1) {
|
|
|
5183
5340
|
|
|
5184
5341
|
//#endregion
|
|
5185
5342
|
//#region src/index.ts
|
|
5186
|
-
|
|
5343
|
+
/**
|
|
5344
|
+
* 获取版本号
|
|
5345
|
+
* 构建后使用 __VERSION__,开发环境从 package.json 读取
|
|
5346
|
+
*/
|
|
5347
|
+
const getVersion = () => {
|
|
5348
|
+
return "5.13.0";
|
|
5349
|
+
};
|
|
5350
|
+
const VERSION = getVersion();
|
|
5187
5351
|
/**
|
|
5188
5352
|
* @deprecated 请使用 createAmagiClient 替代
|
|
5189
5353
|
*/
|
|
@@ -5232,6 +5396,7 @@ exports.BilibiliBv2AvParamsSchema = BilibiliBv2AvParamsSchema;
|
|
|
5232
5396
|
exports.BilibiliColumnInfoParamsSchema = BilibiliColumnInfoParamsSchema;
|
|
5233
5397
|
exports.BilibiliCommentParamsSchema = BilibiliCommentParamsSchema;
|
|
5234
5398
|
exports.BilibiliCommentReplyParamsSchema = BilibiliCommentReplyParamsSchema;
|
|
5399
|
+
exports.BilibiliDanmakuParamsSchema = BilibiliDanmakuParamsSchema;
|
|
5235
5400
|
exports.BilibiliDynamicParamsSchema = BilibiliDynamicParamsSchema;
|
|
5236
5401
|
exports.BilibiliEmojiParamsSchema = BilibiliEmojiParamsSchema;
|
|
5237
5402
|
exports.BilibiliLiveParamsSchema = BilibiliLiveParamsSchema;
|
|
@@ -5302,12 +5467,14 @@ exports.getHeadersAndData = getHeadersAndData;
|
|
|
5302
5467
|
exports.getKuaishouData = getKuaishouData;
|
|
5303
5468
|
exports.handleError = handleError;
|
|
5304
5469
|
exports.httpLogger = httpLogger;
|
|
5470
|
+
exports.initLogger = initLogger;
|
|
5305
5471
|
exports.isNetworkErrorResult = isNetworkErrorResult;
|
|
5306
5472
|
exports.kuaishou = kuaishou;
|
|
5307
5473
|
exports.kuaishouApiUrls = kuaishouApiUrls;
|
|
5308
5474
|
exports.kuaishouUtils = kuaishouUtils;
|
|
5309
5475
|
exports.logMiddleware = logMiddleware;
|
|
5310
5476
|
exports.logger = logger;
|
|
5477
|
+
exports.parseDmSegMobileReply = parseDmSegMobileReply;
|
|
5311
5478
|
exports.qtparam = qtparam;
|
|
5312
5479
|
exports.registerBilibiliRoutes = createBilibiliRoutes;
|
|
5313
5480
|
exports.registerDouyinRoutes = createDouyinRoutes;
|