@ikenxuan/amagi 7.0.0-beta.6 → 7.0.0-beta.7
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/{endpoint-CxOrmO8L.d.ts → api-BWAjDuTS.d.ts} +180 -156
- package/dist/default/index.cjs +1 -1
- package/dist/default/index.d.ts +2 -2
- package/dist/default/index.mjs +1 -1
- package/dist/exports/compat.cjs +1 -1
- package/dist/exports/compat.d.ts +5 -5
- package/dist/exports/compat.mjs +1 -1
- package/dist/exports/sign-steps.d.ts +1 -1
- package/dist/{index-Dh29lDYw.d.ts → index-NkyAecv9.d.ts} +36396 -33179
- package/dist/{src-BHqC-g47.mjs → src-BM1fZuVp.mjs} +192 -85
- package/dist/{src-e3JbCbbg.cjs → src-CNU3-zoM.cjs} +192 -85
- package/package.json +5 -5
|
@@ -9,6 +9,78 @@ import axios, { AxiosError, AxiosHeaders, mergeConfig } from "axios";
|
|
|
9
9
|
import { EventEmitter } from "node:events";
|
|
10
10
|
import { FingerprintGenerator, Xhshow } from "@ikenxuan/xhshow-ts";
|
|
11
11
|
//#region src/platforms/bilibili/api.ts
|
|
12
|
+
/**
|
|
13
|
+
* B站评论区类型代码(`comments` / `commentReplies` 的 `type` 参数)。
|
|
14
|
+
*
|
|
15
|
+
* 完整对照表(部分 oid 含义官方未明确,转写自 bilibili-API-collect):
|
|
16
|
+
*
|
|
17
|
+
* | 代码 | 评论区类型 | oid 的意义 |
|
|
18
|
+
* | --- | --- | --- |
|
|
19
|
+
* | 1 | 视频稿件 | 稿件 avid |
|
|
20
|
+
* | 2 | 话题 | 话题 id |
|
|
21
|
+
* | 4 | 活动 | 活动 id |
|
|
22
|
+
* | 5 | 小视频 | 小视频 id |
|
|
23
|
+
* | 6 | 小黑屋封禁信息 | 封禁公示 id |
|
|
24
|
+
* | 7 | 公告信息 | 公告 id |
|
|
25
|
+
* | 8 | 直播活动 | 直播间 id |
|
|
26
|
+
* | 9 | 活动稿件 | (?) |
|
|
27
|
+
* | 10 | 直播公告 | (?) |
|
|
28
|
+
* | 11 | 相簿(图片动态) | 相簿 id |
|
|
29
|
+
* | 12 | 专栏 | 专栏 cvid |
|
|
30
|
+
* | 13 | 票务 | (?) |
|
|
31
|
+
* | 14 | 音频 | 音频 auid |
|
|
32
|
+
* | 15 | 风纪委员会 | 众裁项目 id |
|
|
33
|
+
* | 16 | 点评 | (?) |
|
|
34
|
+
* | 17 | 动态(纯文字动态&分享) | 动态 id |
|
|
35
|
+
* | 18 | 播单 | (?) |
|
|
36
|
+
* | 19 | 音乐播单 | (?) |
|
|
37
|
+
* | 20 | 漫画 | (?) |
|
|
38
|
+
* | 21 | 漫画 | (?) |
|
|
39
|
+
* | 22 | 漫画 | 漫画 mcid |
|
|
40
|
+
* | 33 | 课程 | 课程 epid |
|
|
41
|
+
*
|
|
42
|
+
* @see https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/comment/readme.md#%E8%AF%84%E8%AE%BA%E5%8C%BA%E7%B1%BB%E5%9E%8B%E4%BB%A3%E7%A0%81
|
|
43
|
+
*/
|
|
44
|
+
const commentTypeUnion = zod.union([
|
|
45
|
+
zod.literal(1),
|
|
46
|
+
zod.literal(2),
|
|
47
|
+
zod.literal(4),
|
|
48
|
+
zod.literal(5),
|
|
49
|
+
zod.literal(6),
|
|
50
|
+
zod.literal(7),
|
|
51
|
+
zod.literal(8),
|
|
52
|
+
zod.literal(9),
|
|
53
|
+
zod.literal(10),
|
|
54
|
+
zod.literal(11),
|
|
55
|
+
zod.literal(12),
|
|
56
|
+
zod.literal(13),
|
|
57
|
+
zod.literal(14),
|
|
58
|
+
zod.literal(15),
|
|
59
|
+
zod.literal(16),
|
|
60
|
+
zod.literal(17),
|
|
61
|
+
zod.literal(18),
|
|
62
|
+
zod.literal(19),
|
|
63
|
+
zod.literal(20),
|
|
64
|
+
zod.literal(21),
|
|
65
|
+
zod.literal(22),
|
|
66
|
+
zod.literal(33)
|
|
67
|
+
], { error: "无效的评论区类型" });
|
|
68
|
+
/**
|
|
69
|
+
* `type` 参数的完整 schema:HTTP query 的字符串先 coerce 成数字,再 pipe 进合法代码
|
|
70
|
+
* 联合收窄 —— SDK 签名拿到的是 `CommentType` 而不是裸 `number`,非法代码报
|
|
71
|
+
* 「无效的评论区类型」。`zod.enum` 在 zod 4.6.5 不接受数字数组(values 恒空、恒报错),
|
|
72
|
+
* 所以用 literal 联合。
|
|
73
|
+
*/
|
|
74
|
+
const commentTypeSchema = zod.coerce.number().pipe(commentTypeUnion);
|
|
75
|
+
/** `mode` 的合法值联合:0/1/2/3 之外(含 coerce 出的 NaN)统一报错 */
|
|
76
|
+
const commentModeUnion = zod.union([
|
|
77
|
+
zod.literal(0),
|
|
78
|
+
zod.literal(1),
|
|
79
|
+
zod.literal(2),
|
|
80
|
+
zod.literal(3)
|
|
81
|
+
], { error: "排序方式只能是 0、1、2、3" });
|
|
82
|
+
/** `mode` 参数的完整 schema:coerce + 收窄到 `0 | 1 | 2 | 3` */
|
|
83
|
+
const commentModeSchema = zod.coerce.number().pipe(commentModeUnion);
|
|
12
84
|
/** B站 API URL 构建类(所有方法只拼 URL,不发起请求) */
|
|
13
85
|
var BilibiliAPI$1 = class {
|
|
14
86
|
/** 获取登录基本信息 */
|
|
@@ -204,6 +276,36 @@ const stepsToSigner = (steps) => {
|
|
|
204
276
|
* @param def - 端点声明
|
|
205
277
|
*/
|
|
206
278
|
const defineEndpoint = (def) => def;
|
|
279
|
+
/**
|
|
280
|
+
* 标记一个端点参数为「内部参数」。
|
|
281
|
+
*
|
|
282
|
+
* 内部参数照常参与 zod 校验、`paginate.nextParams` 的写入与 URL 构造,但三个消费点
|
|
283
|
+
* 都看不见它:
|
|
284
|
+
* - **调用方签名**(fetcher / 静态 fetcher 的 options,见 `PublicParamsOf`)——对象
|
|
285
|
+
* 字面量里传入会被 excess property check 拒掉;
|
|
286
|
+
* - **openapi 的 query parameters**(`parametersOf` 经 `internalParamKeysOf` 过滤);
|
|
287
|
+
* - **文档参数表**(生成器读 openapi 产物,随上一条自动跟随)。
|
|
288
|
+
*
|
|
289
|
+
* 标记与字段声明焊在同一个表达式里(`pagination_str: internalParam(...)`),不存在
|
|
290
|
+
* 「写了字段忘了登记到某张清单」的漂移面 —— 这也是它不做成 EndpointDef 字段的理由:
|
|
291
|
+
* def 上已经够多键了,而且分离的清单必然漂移。运行时经 `.meta()` 挂进 zod 全局
|
|
292
|
+
* registry(文档生成器已在用 meta 传 examples,同一机制)。
|
|
293
|
+
*/
|
|
294
|
+
const internalParam = (schema) => schema.meta({ amagiInternal: true });
|
|
295
|
+
/**
|
|
296
|
+
* 运行时判断一个 params 字段是否内部参数(openapi 生成器的过滤依据)。
|
|
297
|
+
*
|
|
298
|
+
* 读 `.meta()` 而不是别的运行时痕迹:meta 是 zod 官方的元数据通道,克隆与管道都保留。
|
|
299
|
+
*/
|
|
300
|
+
const isInternalParam = (schema) => {
|
|
301
|
+
return (schema?.meta?.())?.amagiInternal === true;
|
|
302
|
+
};
|
|
303
|
+
/** 端点 params 里全部内部参数的键名(运行时)。非 object schema 返回空集 */
|
|
304
|
+
const internalParamKeysOf = (params) => {
|
|
305
|
+
const shape = params.shape;
|
|
306
|
+
if (shape === void 0) return /* @__PURE__ */ new Set();
|
|
307
|
+
return new Set(Object.keys(shape).filter((key) => isInternalParam(shape[key])));
|
|
308
|
+
};
|
|
207
309
|
//#endregion
|
|
208
310
|
//#region src/platforms/bilibili/endpoints/define.ts
|
|
209
311
|
/**
|
|
@@ -508,45 +610,17 @@ const commentReplies$1 = defineBilibiliEndpoint({
|
|
|
508
610
|
},
|
|
509
611
|
params: zod.object({
|
|
510
612
|
oid: zod.string().min(1, { error: "OID不能为空" }).describe("目标对象 ID,视频稿件填 avid"),
|
|
511
|
-
type:
|
|
613
|
+
type: commentTypeSchema.describe("评论区类型代码:1 视频稿件(oid=avid)、11 相簿/图片动态、12 专栏(cvid)、17 动态等;完整对照表见 bilibili-API-collect「评论区类型代码」"),
|
|
512
614
|
root: zod.string().min(1, { error: "根评论ID不能为空" }).describe("根评论 ID,即要展开的一级评论"),
|
|
513
615
|
number: zod.coerce.number().int().positive().default(20).optional().describe("该根评论下的回复条数,不翻页,默认 20")
|
|
514
616
|
}),
|
|
515
617
|
build: (p) => ({
|
|
516
618
|
method: "GET",
|
|
517
|
-
url: bilibiliApiUrls$1.getCommentReplies(
|
|
518
|
-
...p,
|
|
519
|
-
type: p.type
|
|
520
|
-
})
|
|
619
|
+
url: bilibiliApiUrls$1.getCommentReplies(p)
|
|
521
620
|
}),
|
|
522
621
|
retryOn: ["RISK_CONTROL"],
|
|
523
622
|
response: type()
|
|
524
623
|
});
|
|
525
|
-
/** 评论区类型枚举 */
|
|
526
|
-
const COMMENT_TYPES$1 = [
|
|
527
|
-
1,
|
|
528
|
-
2,
|
|
529
|
-
4,
|
|
530
|
-
5,
|
|
531
|
-
6,
|
|
532
|
-
7,
|
|
533
|
-
8,
|
|
534
|
-
9,
|
|
535
|
-
10,
|
|
536
|
-
11,
|
|
537
|
-
12,
|
|
538
|
-
13,
|
|
539
|
-
14,
|
|
540
|
-
15,
|
|
541
|
-
16,
|
|
542
|
-
17,
|
|
543
|
-
18,
|
|
544
|
-
19,
|
|
545
|
-
20,
|
|
546
|
-
21,
|
|
547
|
-
22,
|
|
548
|
-
33
|
|
549
|
-
];
|
|
550
624
|
//#endregion
|
|
551
625
|
//#region src/platforms/bilibili/endpoints/comments.ts
|
|
552
626
|
/**
|
|
@@ -571,21 +645,17 @@ const comments$2 = defineBilibiliEndpoint({
|
|
|
571
645
|
},
|
|
572
646
|
params: zod.object({
|
|
573
647
|
oid: zod.string().min(1, { error: "OID不能为空" }).describe("目标对象 ID,视频稿件填 avid"),
|
|
574
|
-
type:
|
|
648
|
+
type: commentTypeSchema.describe("评论区类型代码:1 视频稿件(oid=avid)、2 话题、11 相簿/图片动态、12 专栏(cvid)、14 音频(auid)、17 动态、33 课程(epid)等;完整对照表见 bilibili-API-collect「评论区类型代码」"),
|
|
575
649
|
number: zod.coerce.number().int().positive().default(20).optional().describe("目标条数,自动翻页合并后去重,默认 20"),
|
|
576
|
-
mode:
|
|
577
|
-
pagination_str: zod.string().optional().describe("翻页游标,由端点接管,不用传"),
|
|
650
|
+
mode: commentModeSchema.optional().describe("排序方式:0 和 3 仅热度,1 按热度+按时间,2 仅时间;默认 3"),
|
|
651
|
+
pagination_str: internalParam(zod.string().optional().describe("翻页游标,由端点接管,不用传")),
|
|
578
652
|
plat: zod.coerce.number().int().optional().describe("平台类型,默认 1"),
|
|
579
653
|
seek_rpid: zod.string().optional().describe("定位到某条评论,默认空"),
|
|
580
654
|
web_location: zod.string().optional().describe("web 位置参数,默认 1315875")
|
|
581
655
|
}),
|
|
582
656
|
build: (p) => ({
|
|
583
657
|
method: "GET",
|
|
584
|
-
url: bilibiliApiUrls$1.getComments(
|
|
585
|
-
...p,
|
|
586
|
-
type: p.type,
|
|
587
|
-
mode: p.mode
|
|
588
|
-
})
|
|
658
|
+
url: bilibiliApiUrls$1.getComments(p)
|
|
589
659
|
}),
|
|
590
660
|
sign: [wbi()],
|
|
591
661
|
paginate: {
|
|
@@ -616,31 +686,6 @@ const comments$2 = defineBilibiliEndpoint({
|
|
|
616
686
|
retryOn: ["RISK_CONTROL"],
|
|
617
687
|
response: type()
|
|
618
688
|
});
|
|
619
|
-
/** 评论区类型枚举 */
|
|
620
|
-
const COMMENT_TYPES = [
|
|
621
|
-
1,
|
|
622
|
-
2,
|
|
623
|
-
4,
|
|
624
|
-
5,
|
|
625
|
-
6,
|
|
626
|
-
7,
|
|
627
|
-
8,
|
|
628
|
-
9,
|
|
629
|
-
10,
|
|
630
|
-
11,
|
|
631
|
-
12,
|
|
632
|
-
13,
|
|
633
|
-
14,
|
|
634
|
-
15,
|
|
635
|
-
16,
|
|
636
|
-
17,
|
|
637
|
-
18,
|
|
638
|
-
19,
|
|
639
|
-
20,
|
|
640
|
-
21,
|
|
641
|
-
22,
|
|
642
|
-
33
|
|
643
|
-
];
|
|
644
689
|
//#endregion
|
|
645
690
|
//#region src/platforms/bilibili/endpoints/dynamicDetail.ts
|
|
646
691
|
/**
|
|
@@ -1382,7 +1427,7 @@ var TypedEventEmitter = class extends EventEmitter {
|
|
|
1382
1427
|
* @description 单例模式,所有模块共享同一个事件总线
|
|
1383
1428
|
* @example
|
|
1384
1429
|
* ```typescript
|
|
1385
|
-
* import { amagiEvents } from '
|
|
1430
|
+
* import { amagiEvents } from '@ikenxuan/amagi'
|
|
1386
1431
|
*
|
|
1387
1432
|
* // 监听 API 成功事件
|
|
1388
1433
|
* amagiEvents.on('api:success', (data) => {
|
|
@@ -3780,7 +3825,15 @@ const SUCCESS_MESSAGE = "获取成功";
|
|
|
3780
3825
|
* `T | undefined`)。数组回调里没有 `if` 可用,`filter` 又只认类型谓词
|
|
3781
3826
|
* —— 这就是必须有守卫的场景:
|
|
3782
3827
|
*
|
|
3783
|
-
* ```ts
|
|
3828
|
+
* ```ts twoslash
|
|
3829
|
+
* import { isSuccess, type AmagiResult } from '@ikenxuan/amagi'
|
|
3830
|
+
*
|
|
3831
|
+
* declare const ids: string[]
|
|
3832
|
+
* declare const fetchOne: (id: string) => Promise<AmagiResult<Work>>
|
|
3833
|
+
* interface Work {
|
|
3834
|
+
* id: string
|
|
3835
|
+
* }
|
|
3836
|
+
*
|
|
3784
3837
|
* const list: AmagiResult<Work>[] = await Promise.all(ids.map(fetchOne))
|
|
3785
3838
|
* const works = list.filter(isSuccess).map((r) => r.data) // Work[]
|
|
3786
3839
|
* ```
|
|
@@ -4106,6 +4159,37 @@ const resolveSigner = (decl, signers) => {
|
|
|
4106
4159
|
return signer;
|
|
4107
4160
|
};
|
|
4108
4161
|
/**
|
|
4162
|
+
* 限额并发地跑异步任务,结果按 `items` 下标顺序收齐(单任务结局语义同
|
|
4163
|
+
* `Promise.allSettled`)。
|
|
4164
|
+
*
|
|
4165
|
+
* 派发顺序仍按 `items` 顺序(FIFO),只是同时在途的不超过 `limit`。单个任务的
|
|
4166
|
+
* 抛错经 `then` 的第二参落进对应下标、不惊动其它任务 —— 不写 `try/catch` 是因为
|
|
4167
|
+
* execute 的「唯一一处 catch」是源码级契约(execute.test.ts 有断言),这里与
|
|
4168
|
+
* `Promise.allSettled` 同构即可。
|
|
4169
|
+
*/
|
|
4170
|
+
const settledWithConcurrency = async (items, limit, task) => {
|
|
4171
|
+
const queue = items.map((item, index) => ({
|
|
4172
|
+
item,
|
|
4173
|
+
index
|
|
4174
|
+
})).reverse();
|
|
4175
|
+
const settled = new Array(items.length);
|
|
4176
|
+
const worker = async () => {
|
|
4177
|
+
while (true) {
|
|
4178
|
+
const job = queue.pop();
|
|
4179
|
+
if (job === void 0) return;
|
|
4180
|
+
settled[job.index] = await task(job.item, job.index).then((value) => ({
|
|
4181
|
+
status: "fulfilled",
|
|
4182
|
+
value
|
|
4183
|
+
}), (reason) => ({
|
|
4184
|
+
status: "rejected",
|
|
4185
|
+
reason
|
|
4186
|
+
}));
|
|
4187
|
+
}
|
|
4188
|
+
};
|
|
4189
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
|
|
4190
|
+
return settled;
|
|
4191
|
+
};
|
|
4192
|
+
/**
|
|
4109
4193
|
* 执行一条端点声明,产出信封。
|
|
4110
4194
|
*
|
|
4111
4195
|
* **永不 reject。** 唯一的例外是调用方自己传入的回调抛出(如会话的
|
|
@@ -4291,15 +4375,18 @@ const execute = async (def, input, options) => {
|
|
|
4291
4375
|
const isMulti = signed.length > 1 || Array.isArray(built);
|
|
4292
4376
|
const reason = isMulti ? "segment" : "initial";
|
|
4293
4377
|
const tolerate = def.partial === "tolerate";
|
|
4378
|
+
const settled = await settledWithConcurrency(signed, 10, (spec, index) => runSpec(spec, reason, refreshFor(params, index)));
|
|
4294
4379
|
let outcomes;
|
|
4295
4380
|
if (tolerate) {
|
|
4296
|
-
outcomes =
|
|
4381
|
+
outcomes = settled.map((r) => r.status === "fulfilled" ? r.value : {
|
|
4297
4382
|
ok: false,
|
|
4298
4383
|
error: classifyThrown(r.reason, "send")
|
|
4299
4384
|
});
|
|
4300
4385
|
if (outcomes.every((o) => !o.ok)) return failWith(outcomes[0].error);
|
|
4301
4386
|
} else {
|
|
4302
|
-
|
|
4387
|
+
const rejected = settled.find((r) => r.status === "rejected");
|
|
4388
|
+
if (rejected) throw rejected.reason;
|
|
4389
|
+
outcomes = settled.map((r) => r.value);
|
|
4303
4390
|
const firstFailure = outcomes.find((o) => !o.ok);
|
|
4304
4391
|
if (firstFailure) return failWith(firstFailure.error);
|
|
4305
4392
|
}
|
|
@@ -5248,7 +5335,7 @@ const commentReplies = defineDouyinEndpoint({
|
|
|
5248
5335
|
aweme_id: zod.string().min(1, { error: "作品ID不能为空" }).describe("作品 ID"),
|
|
5249
5336
|
comment_id: zod.string().min(1, { error: "评论ID不能为空" }).describe("要取回复的评论 ID"),
|
|
5250
5337
|
number: zod.coerce.number().int().min(1).optional().describe("目标条数,默认 3"),
|
|
5251
|
-
cursor: zod.coerce.number().int().min(0).optional().describe("翻页游标,一般不用传")
|
|
5338
|
+
cursor: internalParam(zod.coerce.number().int().min(0).optional().describe("翻页游标,一般不用传"))
|
|
5252
5339
|
}),
|
|
5253
5340
|
build: (p) => ({
|
|
5254
5341
|
method: "GET",
|
|
@@ -5292,7 +5379,7 @@ const comments$1 = defineDouyinEndpoint({
|
|
|
5292
5379
|
params: zod.object({
|
|
5293
5380
|
aweme_id: zod.string().min(1, { error: "作品ID不能为空" }).describe("作品 ID"),
|
|
5294
5381
|
number: zod.coerce.number().int().min(1).optional().describe("目标条数,默认 50"),
|
|
5295
|
-
cursor: zod.coerce.number().int().min(0).optional().describe("翻页游标,一般不用传")
|
|
5382
|
+
cursor: internalParam(zod.coerce.number().int().min(0).optional().describe("翻页游标,一般不用传"))
|
|
5296
5383
|
}),
|
|
5297
5384
|
build: (p) => ({
|
|
5298
5385
|
method: "GET",
|
|
@@ -5320,9 +5407,21 @@ const comments$1 = defineDouyinEndpoint({
|
|
|
5320
5407
|
//#endregion
|
|
5321
5408
|
//#region src/platforms/douyin/endpoints/danmakuList.ts
|
|
5322
5409
|
/**
|
|
5410
|
+
* duration 的硬上限(毫秒,7 天)。
|
|
5411
|
+
*
|
|
5412
|
+
* 纯保险丝:分段并发已由执行器的限额池兜住,真实的超长作品(实测有 27 小时级、
|
|
5413
|
+
* 弹幕正常的直播回放)不受影响。挡的是天文数字的 `duration`(如 `9e15`,能通过
|
|
5414
|
+
* `.int()` 校验)—— `build` 的切段是同步 while 循环,那种值会在任何请求发出之前
|
|
5415
|
+
* 把事件循环卡死、把分段数组撑爆。真实作品离 7 天很远,这个值给足余量。
|
|
5416
|
+
*
|
|
5417
|
+
* 导出是因为校验层的平行 schema(validation/douyin.ts,v6 门面入口)要用同一个数。
|
|
5418
|
+
*/
|
|
5419
|
+
const DANMAKU_MAX_DURATION_MS = 6048e5;
|
|
5420
|
+
/**
|
|
5323
5421
|
* 弹幕列表(分段并发 + 合并排序 + `partial: 'tolerate'`)。
|
|
5324
5422
|
*
|
|
5325
|
-
* 总时长 ≤ 32000ms 单段直取;超过则按 32000ms
|
|
5423
|
+
* 总时长 ≤ 32000ms 单段直取;超过则按 32000ms 切成多段,经执行器的限额并发池
|
|
5424
|
+
* 发出(`SEGMENT_CONCURRENCY`,完成一个补一个),
|
|
5326
5425
|
* **单段失败容忍**(失败段返回 null,其余段照常合并),最后按 `offset_time`
|
|
5327
5426
|
* 升序合并,元信息(`extra` / `log_pb` / `status_code`)取第一段。
|
|
5328
5427
|
*
|
|
@@ -5341,7 +5440,7 @@ const danmakuList$1 = defineDouyinEndpoint({
|
|
|
5341
5440
|
aweme_id: zod.string().min(1, { error: "作品ID不能为空" }).describe("作品 ID"),
|
|
5342
5441
|
start_time: zod.coerce.number().int().min(0).optional().describe("区间起点(毫秒),默认 0"),
|
|
5343
5442
|
end_time: zod.coerce.number().int().min(0).optional().describe("区间终点(毫秒),默认取总时长"),
|
|
5344
|
-
duration: zod.coerce.number().int().min(0).describe("作品总时长(毫秒),也是默认终点")
|
|
5443
|
+
duration: zod.coerce.number().int().min(0).max(DANMAKU_MAX_DURATION_MS, { error: "作品总时长不能超过 7 天" }).describe("作品总时长(毫秒),也是默认终点")
|
|
5345
5444
|
}).refine((data) => data.end_time === void 0 || data.end_time <= data.duration, {
|
|
5346
5445
|
error: "获取弹幕区间的结束时间不能超过视频总时长",
|
|
5347
5446
|
path: ["end_time"]
|
|
@@ -5646,7 +5745,7 @@ const liveRoomInfo$1 = defineDouyinEndpoint({
|
|
|
5646
5745
|
},
|
|
5647
5746
|
params: zod.object({
|
|
5648
5747
|
web_rid: zod.string().min(1, { error: "直播间ID不能为空" }).describe("直播间短号(链接里那段)"),
|
|
5649
|
-
room_id: zod.string().optional().describe("内部透传,一般不用传")
|
|
5748
|
+
room_id: internalParam(zod.string().optional().describe("内部透传,一般不用传"))
|
|
5650
5749
|
}),
|
|
5651
5750
|
build: (p, ctx) => ({
|
|
5652
5751
|
method: "GET",
|
|
@@ -5881,15 +5980,14 @@ const search = defineDouyinEndpoint({
|
|
|
5881
5980
|
"video"
|
|
5882
5981
|
]).default("general").optional().describe("搜索类型,默认综合搜索"),
|
|
5883
5982
|
number: zod.coerce.number().int().min(1).optional().describe("目标条数,默认 15"),
|
|
5884
|
-
search_id: zod.string().optional().describe("翻页游标,一般不用传"),
|
|
5885
|
-
offset: zod.coerce.number().int().min(0).optional().describe("翻页偏移,一般不用传")
|
|
5983
|
+
search_id: internalParam(zod.string().optional().describe("翻页游标,一般不用传")),
|
|
5984
|
+
offset: internalParam(zod.coerce.number().int().min(0).optional().describe("翻页偏移,一般不用传"))
|
|
5886
5985
|
}),
|
|
5887
5986
|
build: (p, ctx) => {
|
|
5888
5987
|
const searchType = p.type ?? "general";
|
|
5889
5988
|
return {
|
|
5890
5989
|
method: "GET",
|
|
5891
5990
|
url: douyinApiUrls$1.search({
|
|
5892
|
-
keyword: p.query,
|
|
5893
5991
|
query: p.query,
|
|
5894
5992
|
type: searchType,
|
|
5895
5993
|
number: p.number,
|
|
@@ -6061,7 +6159,7 @@ const userFavoriteList = defineDouyinEndpoint({
|
|
|
6061
6159
|
params: zod.object({
|
|
6062
6160
|
sec_uid: zod.string().min(1, { error: "用户ID不能为空" }).describe("用户 sec_uid,主页链接里那段"),
|
|
6063
6161
|
number: zod.coerce.number().int().min(1).optional().describe("目标条数,默认 18"),
|
|
6064
|
-
max_cursor: zod.string().optional().describe("翻页游标,一般不用传")
|
|
6162
|
+
max_cursor: internalParam(zod.string().optional().describe("翻页游标,一般不用传"))
|
|
6065
6163
|
}),
|
|
6066
6164
|
build: (p, ctx) => ({
|
|
6067
6165
|
method: "GET",
|
|
@@ -6137,7 +6235,7 @@ const userRecommendList = defineDouyinEndpoint({
|
|
|
6137
6235
|
params: zod.object({
|
|
6138
6236
|
sec_uid: zod.string().min(1, { error: "用户ID不能为空" }).describe("用户 sec_uid,主页链接里那段"),
|
|
6139
6237
|
number: zod.coerce.number().int().min(1).optional().describe("目标条数,默认 18"),
|
|
6140
|
-
max_cursor: zod.string().optional().describe("翻页游标,一般不用传")
|
|
6238
|
+
max_cursor: internalParam(zod.string().optional().describe("翻页游标,一般不用传"))
|
|
6141
6239
|
}),
|
|
6142
6240
|
build: (p, ctx) => ({
|
|
6143
6241
|
method: "GET",
|
|
@@ -6184,7 +6282,7 @@ const userVideoList = defineDouyinEndpoint({
|
|
|
6184
6282
|
params: zod.object({
|
|
6185
6283
|
sec_uid: zod.string().min(1, { error: "用户ID不能为空" }).describe("用户 sec_uid,主页链接里那段"),
|
|
6186
6284
|
number: zod.coerce.number().int().min(1).optional().describe("目标条数,默认 18"),
|
|
6187
|
-
max_cursor: zod.string().optional().describe("翻页游标,一般不用传")
|
|
6285
|
+
max_cursor: internalParam(zod.string().optional().describe("翻页游标,一般不用传"))
|
|
6188
6286
|
}),
|
|
6189
6287
|
build: (p, ctx) => ({
|
|
6190
6288
|
method: "GET",
|
|
@@ -8568,9 +8666,10 @@ const DANMAKU_SCAN_STEP_MS = 6e4;
|
|
|
8568
8666
|
/**
|
|
8569
8667
|
* 扫描范围上限(毫秒,1 小时)。
|
|
8570
8668
|
*
|
|
8571
|
-
*
|
|
8572
|
-
*
|
|
8573
|
-
*
|
|
8669
|
+
* 多窗口经执行器的限额并发池发出(见 execute 的 `SEGMENT_CONCURRENCY`),范围决定
|
|
8670
|
+
* 的是**总**请求数(`ceil(范围 / 60000)`)。不设上限时一个手输的 `duration` 就能让
|
|
8671
|
+
* 一次调用打出上千个请求,所以在参数层挡住:1 小时 → 最多 60 个窗口,与
|
|
8672
|
+
* `userProfile` 的 12 个同量级。
|
|
8574
8673
|
*/
|
|
8575
8674
|
const DANMAKU_MAX_RANGE_MS = 36e5;
|
|
8576
8675
|
/**
|
|
@@ -9801,6 +9900,7 @@ const noteComments = defineXiaohongshuEndpoint({
|
|
|
9801
9900
|
description: "取笔记评论列表。`number` 指定目标条数,翻页由端点自动完成。"
|
|
9802
9901
|
},
|
|
9803
9902
|
params: zod.object({
|
|
9903
|
+
cursor: internalParam(zod.string().optional().describe("分页游标,由端点接管,不用传")),
|
|
9804
9904
|
note_id: zod.string().min(1, { error: "note_id 不能为空" }).describe("笔记 ID;从笔记分享链接里取"),
|
|
9805
9905
|
xsec_token: zod.string().min(1, { error: "xsec_token 不能为空" }).describe("反爬令牌,随笔记分享链接下发"),
|
|
9806
9906
|
number: zod.coerce.number().int().min(1).max(500).optional().describe("目标条数,默认一页")
|
|
@@ -51213,7 +51313,8 @@ const parametersOf = (def) => {
|
|
|
51213
51313
|
unrepresentable: "any"
|
|
51214
51314
|
});
|
|
51215
51315
|
const required = new Set(json.required ?? []);
|
|
51216
|
-
|
|
51316
|
+
const internalKeys = internalParamKeysOf(def.params);
|
|
51317
|
+
return Object.entries(json.properties ?? {}).filter(([name]) => !internalKeys.has(name)).map(([name, schema]) => {
|
|
51217
51318
|
const { description, ...rest } = schema;
|
|
51218
51319
|
return {
|
|
51219
51320
|
name,
|
|
@@ -51522,7 +51623,7 @@ const buildOpenApiSpec = (options = {}) => {
|
|
|
51522
51623
|
openapi: "3.1.0",
|
|
51523
51624
|
info: {
|
|
51524
51625
|
title: "amagi HTTP API",
|
|
51525
|
-
version: options.version ?? "7.0.0-beta.
|
|
51626
|
+
version: options.version ?? "7.0.0-beta.7",
|
|
51526
51627
|
description: [
|
|
51527
51628
|
"由端点注册表派生的规范 —— 手写的那份已经漂移,这份不会(`gen-openapi --check` 进 CI)。",
|
|
51528
51629
|
"",
|
|
@@ -52075,6 +52176,7 @@ const createStaticFetcher = (platform, registry) => {
|
|
|
52075
52176
|
* ```typescript
|
|
52076
52177
|
* import { bilibiliFetcher } from '@ikenxuan/amagi'
|
|
52077
52178
|
*
|
|
52179
|
+
* const cookie = 'SESSDATA=xxx; bili_jct=yyy'
|
|
52078
52180
|
* const result = await bilibiliFetcher.fetchVideoInfo({ bvid: 'BV1xx411c7mD' }, cookie)
|
|
52079
52181
|
* ```
|
|
52080
52182
|
*/
|
|
@@ -52284,6 +52386,7 @@ async function validatePassportVerifyCode(options, cookie, requestConfig) {
|
|
|
52284
52386
|
* ```typescript
|
|
52285
52387
|
* import { douyinFetcher } from '@ikenxuan/amagi'
|
|
52286
52388
|
*
|
|
52389
|
+
* const cookie = 'ttwid=xxx'
|
|
52287
52390
|
* const result = await douyinFetcher.fetchVideoWork({ aweme_id: '7123456789' }, cookie)
|
|
52288
52391
|
* ```
|
|
52289
52392
|
*/
|
|
@@ -52301,6 +52404,8 @@ const douyinFetcher = {
|
|
|
52301
52404
|
* @returns 绑定了 Cookie 的 Fetcher 对象,调用时无需传递 cookie
|
|
52302
52405
|
* @example
|
|
52303
52406
|
* ```typescript
|
|
52407
|
+
* import { createBoundDouyinFetcher } from '@ikenxuan/amagi'
|
|
52408
|
+
*
|
|
52304
52409
|
* const fetcher = createBoundDouyinFetcher('your_cookie')
|
|
52305
52410
|
* const result = await fetcher.fetchVideoWork({ aweme_id: '7123456789' })
|
|
52306
52411
|
* ```
|
|
@@ -52322,6 +52427,7 @@ const createBoundDouyinFetcher = (cookie, requestConfig) => createFetcherFromReg
|
|
|
52322
52427
|
* ```typescript
|
|
52323
52428
|
* import { kuaishouFetcher } from '@ikenxuan/amagi'
|
|
52324
52429
|
*
|
|
52430
|
+
* const cookie = 'did=xxx'
|
|
52325
52431
|
* const result = await kuaishouFetcher.fetchVideoWork({ photoId: '3x123456789' }, cookie)
|
|
52326
52432
|
* ```
|
|
52327
52433
|
*/
|
|
@@ -52344,6 +52450,7 @@ const createBoundKuaishouFetcher = (cookie, requestConfig) => createFetcherFromR
|
|
|
52344
52450
|
* ```typescript
|
|
52345
52451
|
* import { xiaohongshuFetcher } from '@ikenxuan/amagi'
|
|
52346
52452
|
*
|
|
52453
|
+
* const cookie = 'a1=xxx; web_session=xxx'
|
|
52347
52454
|
* const result = await xiaohongshuFetcher.fetchNoteDetail({ note_id: 'n1', xsec_token: 'tk' }, cookie)
|
|
52348
52455
|
* ```
|
|
52349
52456
|
*/
|
|
@@ -52738,7 +52845,7 @@ const DouyinValidationSchemas = {
|
|
|
52738
52845
|
aweme_id: zod.string({ error: "视频ID必须是字符串" }).min(1, { error: "视频ID不能为空" }),
|
|
52739
52846
|
start_time: zod.coerce.number({ error: "开始时间必须是数字" }).int({ error: "开始时间必须是整数" }).min(0, { error: "开始时间不能小于0" }).optional(),
|
|
52740
52847
|
end_time: zod.coerce.number({ error: "结束时间必须是数字" }).int({ error: "结束时间必须是整数" }).min(0, { error: "结束时间不能小于0" }).optional(),
|
|
52741
|
-
duration: zod.coerce.number({ error: "视频时长必须是数字" }).int({ error: "视频时长必须是整数" }).min(0, { error: "视频时长不能小于0" })
|
|
52848
|
+
duration: zod.coerce.number({ error: "视频时长必须是数字" }).int({ error: "视频时长必须是整数" }).min(0, { error: "视频时长不能小于0" }).max(DANMAKU_MAX_DURATION_MS, { error: "作品总时长不能超过 7 天" })
|
|
52742
52849
|
}).refine((data) => {
|
|
52743
52850
|
if (data.end_time !== void 0) return data.end_time <= data.duration;
|
|
52744
52851
|
return true;
|
|
@@ -54286,7 +54393,7 @@ let DynamicType = /* @__PURE__ */ function(DynamicType) {
|
|
|
54286
54393
|
* 构建后使用 __VERSION__,开发环境从 package.json 读取
|
|
54287
54394
|
*/
|
|
54288
54395
|
const getVersion = () => {
|
|
54289
|
-
return "7.0.0-beta.
|
|
54396
|
+
return "7.0.0-beta.7";
|
|
54290
54397
|
};
|
|
54291
54398
|
const VERSION = getVersion();
|
|
54292
54399
|
/**
|