@ikenxuan/amagi 7.0.0-beta.3 → 7.0.0-beta.5
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 +1 -1
- package/dist/default/index.d.ts +1 -1
- package/dist/default/index.mjs +1 -1
- package/dist/exports/compat.cjs +1 -1
- package/dist/exports/compat.d.ts +7022 -127
- package/dist/exports/compat.mjs +1 -1
- package/dist/exports/signing.d.ts +58 -58
- package/dist/{index-CySQMTrB.d.ts → index-TDTBTnNP.d.ts} +11677 -258
- package/dist/{src-bG9knrUJ.cjs → src-DixrwqqA.cjs} +251 -231
- package/dist/{src-B9Len1oc.mjs → src-blqLvYiW.mjs} +251 -231
- package/package.json +6 -13
- package/dist/exports/chalk.cjs +0 -9
- package/dist/exports/chalk.d.ts +0 -1
- package/dist/exports/chalk.mjs +0 -2
|
@@ -176,15 +176,25 @@ const bilibiliApiUrls$1 = new BilibiliAPI$1();
|
|
|
176
176
|
*/
|
|
177
177
|
const type = () => ({});
|
|
178
178
|
/**
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
* `TData` 从 `response` / `normalize` / `compute` 推出。
|
|
179
|
+
* 声明一个端点。运行时是恒等函数(原样返回 `def`),全部价值在类型推导:
|
|
180
|
+
* `TParams` 从 `params` 推、`TData` 从 `response` / `compute` 推。
|
|
181
|
+
* 平台一般用各自的 `defineXxxEndpoint`(把签名器名收窄),不直接用这个。
|
|
183
182
|
* @param def - 端点声明
|
|
184
|
-
* @returns 原样返回 `def`,但带上推导好的具体类型
|
|
185
183
|
*/
|
|
186
184
|
const defineEndpoint = (def) => def;
|
|
187
185
|
//#endregion
|
|
186
|
+
//#region src/platforms/bilibili/endpoints/define.ts
|
|
187
|
+
/**
|
|
188
|
+
* B站端点声明。
|
|
189
|
+
*
|
|
190
|
+
* 就是 {@link defineEndpoint} 绑定了 B站签名器名联合 {@link BilibiliSignerName}:
|
|
191
|
+
* `sign` 的字符串分支被收窄成 `'wbi' | 'qtparam'`,写错名字编译期即报错,不必等
|
|
192
|
+
* 运行时查表失败。运行时仍是恒等函数,其余行为与通用 `defineEndpoint` 完全一致。
|
|
193
|
+
* @param def - 端点声明(`sign` 的名字受 B站签名器表约束)
|
|
194
|
+
* @returns 原样返回 `def`,带上推导好的具体类型
|
|
195
|
+
*/
|
|
196
|
+
const defineBilibiliEndpoint = (def) => defineEndpoint(def);
|
|
197
|
+
//#endregion
|
|
188
198
|
//#region src/platforms/bilibili/endpoints/articleCards.ts
|
|
189
199
|
/**
|
|
190
200
|
* 专栏显示卡片信息(单请求)。
|
|
@@ -192,7 +202,7 @@ const defineEndpoint = (def) => def;
|
|
|
192
202
|
* 与旧版一致:`getArticleCards` GET,无签名。
|
|
193
203
|
* `ids` 可传字符串或字符串数组。
|
|
194
204
|
*/
|
|
195
|
-
const articleCards =
|
|
205
|
+
const articleCards = defineBilibiliEndpoint({
|
|
196
206
|
name: "bilibili.articleCards",
|
|
197
207
|
route: "/fetch_article_card",
|
|
198
208
|
doc: {
|
|
@@ -214,7 +224,7 @@ const articleCards = defineEndpoint({
|
|
|
214
224
|
*
|
|
215
225
|
* 与旧版一致:`getArticleContent` GET,无签名。
|
|
216
226
|
*/
|
|
217
|
-
const articleContent =
|
|
227
|
+
const articleContent = defineBilibiliEndpoint({
|
|
218
228
|
name: "bilibili.articleContent",
|
|
219
229
|
route: "/fetch_article_content",
|
|
220
230
|
doc: {
|
|
@@ -236,7 +246,7 @@ const articleContent = defineEndpoint({
|
|
|
236
246
|
*
|
|
237
247
|
* 与旧版一致:`getArticleInfo` GET,无签名。
|
|
238
248
|
*/
|
|
239
|
-
const articleInfo =
|
|
249
|
+
const articleInfo = defineBilibiliEndpoint({
|
|
240
250
|
name: "bilibili.articleInfo",
|
|
241
251
|
route: "/fetch_article_info",
|
|
242
252
|
doc: {
|
|
@@ -258,7 +268,7 @@ const articleInfo = defineEndpoint({
|
|
|
258
268
|
*
|
|
259
269
|
* 与旧版一致:`getArticleListInfo` GET,无签名。
|
|
260
270
|
*/
|
|
261
|
-
const articleListInfo =
|
|
271
|
+
const articleListInfo = defineBilibiliEndpoint({
|
|
262
272
|
name: "bilibili.articleListInfo",
|
|
263
273
|
route: "/fetch_column_info",
|
|
264
274
|
doc: {
|
|
@@ -334,7 +344,7 @@ const bv2av = (bvid) => {
|
|
|
334
344
|
*
|
|
335
345
|
* 返回形状:`{ bvid }`。
|
|
336
346
|
*/
|
|
337
|
-
const avToBv =
|
|
347
|
+
const avToBv = defineBilibiliEndpoint({
|
|
338
348
|
name: "bilibili.avToBv",
|
|
339
349
|
route: "/av_to_bv",
|
|
340
350
|
doc: {
|
|
@@ -353,7 +363,7 @@ const avToBv = defineEndpoint({
|
|
|
353
363
|
* 与旧版一致:`getBangumiInfo` GET,无签名。
|
|
354
364
|
* `ep_id` 与 `season_id` 至少传其一,两者都要求非空串(`ep_id` 优先)。
|
|
355
365
|
*/
|
|
356
|
-
const bangumiInfo =
|
|
366
|
+
const bangumiInfo = defineBilibiliEndpoint({
|
|
357
367
|
name: "bilibili.bangumiInfo",
|
|
358
368
|
route: "/fetch_bangumi_video_info",
|
|
359
369
|
doc: {
|
|
@@ -387,7 +397,7 @@ const bangumiInfo = defineEndpoint({
|
|
|
387
397
|
* 与旧版一致:`getBangumiStream` GET + qtparam 签名,
|
|
388
398
|
* `ep_id` 去掉 `ep` 前缀。
|
|
389
399
|
*/
|
|
390
|
-
const bangumiStream =
|
|
400
|
+
const bangumiStream = defineBilibiliEndpoint({
|
|
391
401
|
name: "bilibili.bangumiStream",
|
|
392
402
|
route: "/fetch_bangumi_video_playurl",
|
|
393
403
|
doc: {
|
|
@@ -418,7 +428,7 @@ const bangumiStream = defineEndpoint({
|
|
|
418
428
|
*
|
|
419
429
|
* 返回 `{ aid: number }`,不带 `av` 前缀。
|
|
420
430
|
*/
|
|
421
|
-
const bvToAv =
|
|
431
|
+
const bvToAv = defineBilibiliEndpoint({
|
|
422
432
|
name: "bilibili.bvToAv",
|
|
423
433
|
route: "/bv_to_av",
|
|
424
434
|
doc: {
|
|
@@ -437,7 +447,7 @@ const bvToAv = defineEndpoint({
|
|
|
437
447
|
* 与旧版一致:`getCaptchaFromVoucher` POST,
|
|
438
448
|
* body 为 `{ csrf?, v_voucher }`,无签名。
|
|
439
449
|
*/
|
|
440
|
-
const captchaFromVoucher =
|
|
450
|
+
const captchaFromVoucher = defineBilibiliEndpoint({
|
|
441
451
|
name: "bilibili.captchaFromVoucher",
|
|
442
452
|
route: "/apply_captcha",
|
|
443
453
|
doc: {
|
|
@@ -467,7 +477,7 @@ const captchaFromVoucher = defineEndpoint({
|
|
|
467
477
|
*
|
|
468
478
|
* 与旧版一致:`getCommentReplies` GET,无签名。
|
|
469
479
|
*/
|
|
470
|
-
const commentReplies$1 =
|
|
480
|
+
const commentReplies$1 = defineBilibiliEndpoint({
|
|
471
481
|
name: "bilibili.commentReplies",
|
|
472
482
|
route: "/fetch_comment_reply",
|
|
473
483
|
doc: {
|
|
@@ -530,7 +540,7 @@ const COMMENT_TYPES$1 = [
|
|
|
530
540
|
* 按 `rpid` 去重 + 截断到 `number`。
|
|
531
541
|
* - 未开放评论区由主接口返回非 0 的 `code`,由 judge 判失败。
|
|
532
542
|
*/
|
|
533
|
-
const comments$2 =
|
|
543
|
+
const comments$2 = defineBilibiliEndpoint({
|
|
534
544
|
name: "bilibili.comments",
|
|
535
545
|
route: "/fetch_work_comments",
|
|
536
546
|
doc: {
|
|
@@ -569,20 +579,18 @@ const comments$2 = defineEndpoint({
|
|
|
569
579
|
...params,
|
|
570
580
|
pagination_str: next ?? params.pagination_str
|
|
571
581
|
};
|
|
582
|
+
},
|
|
583
|
+
merge: ({ lastPage, items }, params) => {
|
|
584
|
+
const sliced = Array.from(new Map(items.map((item) => [item.rpid, item])).values()).slice(0, params.number ?? 20);
|
|
585
|
+
return {
|
|
586
|
+
...lastPage,
|
|
587
|
+
data: {
|
|
588
|
+
...lastPage.data,
|
|
589
|
+
replies: sliced
|
|
590
|
+
}
|
|
591
|
+
};
|
|
572
592
|
}
|
|
573
593
|
},
|
|
574
|
-
normalize: (decoded, params) => {
|
|
575
|
-
const { lastPage, items } = decoded;
|
|
576
|
-
const page = lastPage;
|
|
577
|
-
const sliced = Array.from(new Map(items.map((item) => [item.rpid, item])).values()).slice(0, params.number ?? 20);
|
|
578
|
-
return {
|
|
579
|
-
...page ?? {},
|
|
580
|
-
data: {
|
|
581
|
-
...page?.data ?? {},
|
|
582
|
-
replies: sliced
|
|
583
|
-
}
|
|
584
|
-
};
|
|
585
|
-
},
|
|
586
594
|
retryOn: ["RISK_CONTROL"],
|
|
587
595
|
response: type()
|
|
588
596
|
});
|
|
@@ -618,7 +626,7 @@ const COMMENT_TYPES = [
|
|
|
618
626
|
*
|
|
619
627
|
* 与旧版一致:`getDynamicDetail` GET,无签名。
|
|
620
628
|
*/
|
|
621
|
-
const dynamicDetail =
|
|
629
|
+
const dynamicDetail = defineBilibiliEndpoint({
|
|
622
630
|
name: "bilibili.dynamicDetail",
|
|
623
631
|
route: "/fetch_dynamic_info",
|
|
624
632
|
doc: {
|
|
@@ -641,7 +649,7 @@ const dynamicDetail = defineEndpoint({
|
|
|
641
649
|
* 与旧版一致:`getEmojiList` GET,无签名。
|
|
642
650
|
* 它是网络请求(表情面板接口),不是本地计算。
|
|
643
651
|
*/
|
|
644
|
-
const emojiList$4 =
|
|
652
|
+
const emojiList$4 = defineBilibiliEndpoint({
|
|
645
653
|
name: "bilibili.emojiList",
|
|
646
654
|
route: "/fetch_emoji_list",
|
|
647
655
|
doc: {
|
|
@@ -663,7 +671,7 @@ const emojiList$4 = defineEndpoint({
|
|
|
663
671
|
*
|
|
664
672
|
* 与旧版一致:`getLiveRoomInfo` GET,无签名。
|
|
665
673
|
*/
|
|
666
|
-
const liveRoomInfo$2 =
|
|
674
|
+
const liveRoomInfo$2 = defineBilibiliEndpoint({
|
|
667
675
|
name: "bilibili.liveRoomInfo",
|
|
668
676
|
route: "/fetch_live_room_detail",
|
|
669
677
|
doc: {
|
|
@@ -685,7 +693,7 @@ const liveRoomInfo$2 = defineEndpoint({
|
|
|
685
693
|
*
|
|
686
694
|
* 与旧版一致:`getLiveRoomInit` GET,无签名。
|
|
687
695
|
*/
|
|
688
|
-
const liveRoomInit =
|
|
696
|
+
const liveRoomInit = defineBilibiliEndpoint({
|
|
689
697
|
name: "bilibili.liveRoomInit",
|
|
690
698
|
route: "/fetch_liveroom_def",
|
|
691
699
|
doc: {
|
|
@@ -707,7 +715,7 @@ const liveRoomInit = defineEndpoint({
|
|
|
707
715
|
*
|
|
708
716
|
* 与旧版一致:`getLoginQrcode` GET,无签名。
|
|
709
717
|
*/
|
|
710
|
-
const loginQrcode$1 =
|
|
718
|
+
const loginQrcode$1 = defineBilibiliEndpoint({
|
|
711
719
|
name: "bilibili.loginQrcode",
|
|
712
720
|
route: "/new_login_qrcode",
|
|
713
721
|
doc: {
|
|
@@ -729,7 +737,7 @@ const loginQrcode$1 = defineEndpoint({
|
|
|
729
737
|
*
|
|
730
738
|
* 与旧版一致:`getLoginStatus` GET,无签名。
|
|
731
739
|
*/
|
|
732
|
-
const loginStatus =
|
|
740
|
+
const loginStatus = defineBilibiliEndpoint({
|
|
733
741
|
name: "bilibili.loginStatus",
|
|
734
742
|
route: "/login_basic_info",
|
|
735
743
|
doc: {
|
|
@@ -752,7 +760,7 @@ const loginStatus = defineEndpoint({
|
|
|
752
760
|
* 与旧版一致:`getQrcodeStatus` GET,无签名。
|
|
753
761
|
* **返回形状**:只返回平台响应体(`code` / `data` / `message`),不透出响应 headers。
|
|
754
762
|
*/
|
|
755
|
-
const qrcodeStatus =
|
|
763
|
+
const qrcodeStatus = defineBilibiliEndpoint({
|
|
756
764
|
name: "bilibili.qrcodeStatus",
|
|
757
765
|
route: "/check_qrcode",
|
|
758
766
|
doc: {
|
|
@@ -774,7 +782,7 @@ const qrcodeStatus = defineEndpoint({
|
|
|
774
782
|
*
|
|
775
783
|
* 与旧版一致:`getUploaderTotalViews` GET,无签名。
|
|
776
784
|
*/
|
|
777
|
-
const uploaderTotalViews =
|
|
785
|
+
const uploaderTotalViews = defineBilibiliEndpoint({
|
|
778
786
|
name: "bilibili.uploaderTotalViews",
|
|
779
787
|
route: "/fetch_user_full_view",
|
|
780
788
|
doc: {
|
|
@@ -796,7 +804,7 @@ const uploaderTotalViews = defineEndpoint({
|
|
|
796
804
|
*
|
|
797
805
|
* 与旧版一致:`getUserCard` GET,无签名。
|
|
798
806
|
*/
|
|
799
|
-
const userCard =
|
|
807
|
+
const userCard = defineBilibiliEndpoint({
|
|
800
808
|
name: "bilibili.userCard",
|
|
801
809
|
route: "/fetch_user_profile",
|
|
802
810
|
doc: {
|
|
@@ -927,7 +935,7 @@ var AmagiHeaders = class AmagiHeaders {
|
|
|
927
935
|
* `Referer: https://space.bilibili.com/{host_mid}/dynamic`
|
|
928
936
|
* (调用方显式传了 Referer 则不覆盖)。
|
|
929
937
|
*/
|
|
930
|
-
const userDynamicList =
|
|
938
|
+
const userDynamicList = defineBilibiliEndpoint({
|
|
931
939
|
name: "bilibili.userDynamicList",
|
|
932
940
|
route: "/fetch_user_dynamic",
|
|
933
941
|
doc: {
|
|
@@ -956,7 +964,7 @@ const userDynamicList = defineEndpoint({
|
|
|
956
964
|
*
|
|
957
965
|
* 与旧版一致:`getUserLiveStatus` GET,无签名。
|
|
958
966
|
*/
|
|
959
|
-
const userLiveStatus =
|
|
967
|
+
const userLiveStatus = defineBilibiliEndpoint({
|
|
960
968
|
name: "bilibili.userLiveStatus",
|
|
961
969
|
route: "/fetch_user_live_status",
|
|
962
970
|
doc: {
|
|
@@ -978,7 +986,7 @@ const userLiveStatus = defineEndpoint({
|
|
|
978
986
|
*
|
|
979
987
|
* 与旧版一致:`getUserSpaceInfo` GET + wbi 签名。
|
|
980
988
|
*/
|
|
981
|
-
const userSpaceInfo =
|
|
989
|
+
const userSpaceInfo = defineBilibiliEndpoint({
|
|
982
990
|
name: "bilibili.userSpaceInfo",
|
|
983
991
|
route: "/fetch_user_space_info",
|
|
984
992
|
doc: {
|
|
@@ -1002,7 +1010,7 @@ const userSpaceInfo = defineEndpoint({
|
|
|
1002
1010
|
* 与旧版一致:`validateCaptcha` POST,
|
|
1003
1011
|
* body 为 `{ challenge, token, validate, seccode, csrf? }`,无签名。
|
|
1004
1012
|
*/
|
|
1005
|
-
const validateCaptcha =
|
|
1013
|
+
const validateCaptcha = defineBilibiliEndpoint({
|
|
1006
1014
|
name: "bilibili.validateCaptcha",
|
|
1007
1015
|
route: "/validate_captcha",
|
|
1008
1016
|
doc: {
|
|
@@ -1139,7 +1147,7 @@ function parseDmSegMobileReply(data) {
|
|
|
1139
1147
|
* judge 恒成功:二进制响应没有 `code` 字段,交给 decode 解析;
|
|
1140
1148
|
* 解析失败由 execute 归因为 `parse` / `DECODE_FAILED`。
|
|
1141
1149
|
*/
|
|
1142
|
-
const videoDanmaku =
|
|
1150
|
+
const videoDanmaku = defineBilibiliEndpoint({
|
|
1143
1151
|
name: "bilibili.videoDanmaku",
|
|
1144
1152
|
route: "/fetch_danmaku",
|
|
1145
1153
|
doc: {
|
|
@@ -1169,7 +1177,7 @@ const videoDanmaku = defineEndpoint({
|
|
|
1169
1177
|
* 共 27 条路由。
|
|
1170
1178
|
*/
|
|
1171
1179
|
const bilibiliRegistry = {
|
|
1172
|
-
videoInfo:
|
|
1180
|
+
videoInfo: defineBilibiliEndpoint({
|
|
1173
1181
|
name: "bilibili.videoInfo",
|
|
1174
1182
|
route: "/fetch_one_video",
|
|
1175
1183
|
doc: {
|
|
@@ -1184,7 +1192,7 @@ const bilibiliRegistry = {
|
|
|
1184
1192
|
retryOn: ["RISK_CONTROL"],
|
|
1185
1193
|
response: type()
|
|
1186
1194
|
}),
|
|
1187
|
-
videoStream:
|
|
1195
|
+
videoStream: defineBilibiliEndpoint({
|
|
1188
1196
|
name: "bilibili.videoStream",
|
|
1189
1197
|
route: "/fetch_video_playurl",
|
|
1190
1198
|
doc: {
|
|
@@ -3522,7 +3530,12 @@ const xBogusSigner = (spec, ctx) => {
|
|
|
3522
3530
|
url: url.toString()
|
|
3523
3531
|
}, ctx.cookie);
|
|
3524
3532
|
};
|
|
3525
|
-
/**
|
|
3533
|
+
/**
|
|
3534
|
+
* 平台签名器表,交给 runtime 的 `signers` 查名。
|
|
3535
|
+
*
|
|
3536
|
+
* 用 `satisfies` 而非显式 `: Record<string, SignFn>` 返回:后者会把键联合抹成宽
|
|
3537
|
+
* `string`,而 {@link DouyinSignerName} 要靠 `keyof` 从这张表推导出精确的名字联合。
|
|
3538
|
+
*/
|
|
3526
3539
|
const createDouyinSigners = () => ({
|
|
3527
3540
|
"a-bogus": aBogusSigner,
|
|
3528
3541
|
"x-bogus": xBogusSigner
|
|
@@ -5466,7 +5479,12 @@ const xhsGetXywTraceSigner = async (spec, ctx) => {
|
|
|
5466
5479
|
headers: headers.toJSON()
|
|
5467
5480
|
};
|
|
5468
5481
|
};
|
|
5469
|
-
/**
|
|
5482
|
+
/**
|
|
5483
|
+
* 平台签名器表,交给 runtime 的 `signers` 查名。
|
|
5484
|
+
*
|
|
5485
|
+
* 用 `satisfies` 而非显式 `: Record<string, SignFn>` 返回:后者会把键联合抹成宽
|
|
5486
|
+
* `string`,而 {@link XiaohongshuSignerName} 要靠 `keyof` 从这张表推导精确名字联合。
|
|
5487
|
+
*/
|
|
5470
5488
|
const createXiaohongshuSigners = () => ({
|
|
5471
5489
|
"xhs-post": xhsPostSigner,
|
|
5472
5490
|
"xhs-get": xhsGetSigner,
|
|
@@ -6383,7 +6401,10 @@ const runPaginated = async (def, params, runPage) => {
|
|
|
6383
6401
|
const limitKey = def.limitParam ?? "number";
|
|
6384
6402
|
const countKey = def.countParam ?? limitKey;
|
|
6385
6403
|
const target = resolveTarget(params[limitKey], def.maxPageSize);
|
|
6386
|
-
|
|
6404
|
+
if (target === 0) return {
|
|
6405
|
+
ok: true,
|
|
6406
|
+
value: { empty: true }
|
|
6407
|
+
};
|
|
6387
6408
|
const collected = [];
|
|
6388
6409
|
let current = params;
|
|
6389
6410
|
let lastPage;
|
|
@@ -6400,7 +6421,6 @@ const runPaginated = async (def, params, runPage) => {
|
|
|
6400
6421
|
error: outcome.error
|
|
6401
6422
|
};
|
|
6402
6423
|
const page = outcome.value;
|
|
6403
|
-
pages.push(page);
|
|
6404
6424
|
lastPage = page;
|
|
6405
6425
|
const list = def.items(page);
|
|
6406
6426
|
if (Array.isArray(list) && list.length > 0) collected.push(...list);
|
|
@@ -6412,9 +6432,9 @@ const runPaginated = async (def, params, runPage) => {
|
|
|
6412
6432
|
return {
|
|
6413
6433
|
ok: true,
|
|
6414
6434
|
value: {
|
|
6435
|
+
empty: false,
|
|
6415
6436
|
lastPage,
|
|
6416
|
-
|
|
6417
|
-
items: target === 0 ? [] : collected.slice(0, target)
|
|
6437
|
+
items: collected.slice(0, target)
|
|
6418
6438
|
}
|
|
6419
6439
|
};
|
|
6420
6440
|
};
|
|
@@ -6766,7 +6786,10 @@ const execute = async (def, input, options) => {
|
|
|
6766
6786
|
});
|
|
6767
6787
|
if (!paged.ok) return failWith(paged.error);
|
|
6768
6788
|
stage = "normalize";
|
|
6769
|
-
|
|
6789
|
+
const outcome = paged.value;
|
|
6790
|
+
if (outcome.empty) return succeed(void 0);
|
|
6791
|
+
const merge = def.paginate.merge;
|
|
6792
|
+
return succeed(merge ? merge(outcome, params) : outcome);
|
|
6770
6793
|
}
|
|
6771
6794
|
stage = "build";
|
|
6772
6795
|
const built = def.build(params, ctx);
|
|
@@ -6790,6 +6813,15 @@ const execute = async (def, input, options) => {
|
|
|
6790
6813
|
if (firstFailure) return failWith(firstFailure.error);
|
|
6791
6814
|
}
|
|
6792
6815
|
stage = "normalize";
|
|
6816
|
+
if (isMulti && def.aggregate) {
|
|
6817
|
+
const parts = outcomes.map((o) => o.ok ? o.value : void 0);
|
|
6818
|
+
const head = parts.find((p) => p !== void 0);
|
|
6819
|
+
const aggregate = def.aggregate;
|
|
6820
|
+
return succeed(aggregate({
|
|
6821
|
+
parts,
|
|
6822
|
+
head
|
|
6823
|
+
}, params));
|
|
6824
|
+
}
|
|
6793
6825
|
const decodedValue = isMulti ? outcomes.map((o) => o.ok ? o.value : void 0) : outcomes[0].value;
|
|
6794
6826
|
return succeed(def.normalize ? def.normalize(decodedValue, params) : decodedValue);
|
|
6795
6827
|
} catch (cause) {
|
|
@@ -7703,6 +7735,19 @@ var DouyinAPI$1 = class {
|
|
|
7703
7735
|
/** 默认的 DouyinAPI 实例(使用默认浏览器版本 125.0.0.0) */
|
|
7704
7736
|
const douyinApiUrls$1 = new DouyinAPI$1();
|
|
7705
7737
|
//#endregion
|
|
7738
|
+
//#region src/platforms/douyin/endpoints/define.ts
|
|
7739
|
+
/**
|
|
7740
|
+
* 抖音端点声明。
|
|
7741
|
+
*
|
|
7742
|
+
* 就是 {@link defineEndpoint} 绑定了抖音签名器名联合 {@link DouyinSignerName}:
|
|
7743
|
+
* `sign` 的字符串分支被收窄成 `'a-bogus' | 'x-bogus'`,写错名字(如 `'a-bogas'`)
|
|
7744
|
+
* 编译期即报错,不必等运行时查表失败。运行时仍是恒等函数,其余行为与通用
|
|
7745
|
+
* `defineEndpoint` 完全一致。
|
|
7746
|
+
* @param def - 端点声明(`sign` 的名字受抖音签名器表约束)
|
|
7747
|
+
* @returns 原样返回 `def`,带上推导好的具体类型
|
|
7748
|
+
*/
|
|
7749
|
+
const defineDouyinEndpoint = (def) => defineEndpoint(def);
|
|
7750
|
+
//#endregion
|
|
7706
7751
|
//#region src/platforms/douyin/endpoints/commentReplies.ts
|
|
7707
7752
|
/**
|
|
7708
7753
|
* 二级评论(声明式翻页,maxPageSize 3,签名用 `x_bogus`)。
|
|
@@ -7710,7 +7755,7 @@ const douyinApiUrls$1 = new DouyinAPI$1();
|
|
|
7710
7755
|
* 与旧版一致:`getCommentReplies` GET,签名器是 `x_bogus`,游标是 `cursor`
|
|
7711
7756
|
* (`has_more === 1` 继续),最终形状 `{ ...最后一页, comments, cursor }`。
|
|
7712
7757
|
*/
|
|
7713
|
-
const commentReplies =
|
|
7758
|
+
const commentReplies = defineDouyinEndpoint({
|
|
7714
7759
|
name: "douyin.commentReplies",
|
|
7715
7760
|
route: "/fetch_video_comment_replies",
|
|
7716
7761
|
doc: {
|
|
@@ -7737,16 +7782,12 @@ const commentReplies = defineEndpoint({
|
|
|
7737
7782
|
nextParams: (params, page) => ({
|
|
7738
7783
|
...params,
|
|
7739
7784
|
cursor: page.cursor
|
|
7740
|
-
})
|
|
7741
|
-
|
|
7742
|
-
|
|
7743
|
-
const { lastPage, items } = decoded;
|
|
7744
|
-
const page = lastPage;
|
|
7745
|
-
return {
|
|
7746
|
-
...page ?? {},
|
|
7785
|
+
}),
|
|
7786
|
+
merge: ({ lastPage, items }) => ({
|
|
7787
|
+
...lastPage,
|
|
7747
7788
|
comments: items,
|
|
7748
|
-
cursor:
|
|
7749
|
-
}
|
|
7789
|
+
cursor: lastPage.cursor ?? items.length
|
|
7790
|
+
})
|
|
7750
7791
|
},
|
|
7751
7792
|
response: type()
|
|
7752
7793
|
});
|
|
@@ -7759,7 +7800,7 @@ const commentReplies = defineEndpoint({
|
|
|
7759
7800
|
* `cursor`(`has_more === 1` 继续),最终形状 `{ ...最后一页, comments, cursor }`
|
|
7760
7801
|
* (`cursor: resp.cursor ?? list.length`)。
|
|
7761
7802
|
*/
|
|
7762
|
-
const comments$1 =
|
|
7803
|
+
const comments$1 = defineDouyinEndpoint({
|
|
7763
7804
|
name: "douyin.comments",
|
|
7764
7805
|
route: "/fetch_work_comments",
|
|
7765
7806
|
doc: {
|
|
@@ -7785,16 +7826,12 @@ const comments$1 = defineEndpoint({
|
|
|
7785
7826
|
nextParams: (params, page) => ({
|
|
7786
7827
|
...params,
|
|
7787
7828
|
cursor: page.cursor
|
|
7788
|
-
})
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
const { lastPage, items } = decoded;
|
|
7792
|
-
const page = lastPage;
|
|
7793
|
-
return {
|
|
7794
|
-
...page ?? {},
|
|
7829
|
+
}),
|
|
7830
|
+
merge: ({ lastPage, items }) => ({
|
|
7831
|
+
...lastPage,
|
|
7795
7832
|
comments: items,
|
|
7796
|
-
cursor:
|
|
7797
|
-
}
|
|
7833
|
+
cursor: lastPage.cursor ?? items.length
|
|
7834
|
+
})
|
|
7798
7835
|
},
|
|
7799
7836
|
response: type()
|
|
7800
7837
|
});
|
|
@@ -7811,7 +7848,7 @@ const comments$1 = defineEndpoint({
|
|
|
7811
7848
|
* 整体(execute 的 tolerate:**全部分片都失败时仍返回失败信封**),
|
|
7812
7849
|
* `normalize` 负责合并与排序。
|
|
7813
7850
|
*/
|
|
7814
|
-
const danmakuList$1 =
|
|
7851
|
+
const danmakuList$1 = defineDouyinEndpoint({
|
|
7815
7852
|
name: "douyin.danmakuList",
|
|
7816
7853
|
route: "/fetch_work_danmaku",
|
|
7817
7854
|
doc: {
|
|
@@ -7863,33 +7900,14 @@ const danmakuList$1 = defineEndpoint({
|
|
|
7863
7900
|
retryOn: ["ANTIBOT_PAGE"],
|
|
7864
7901
|
retryFresh: true,
|
|
7865
7902
|
partial: "tolerate",
|
|
7866
|
-
|
|
7867
|
-
const
|
|
7868
|
-
const startTime = params.start_time ?? 0;
|
|
7869
|
-
const endTime = params.end_time ?? params.duration;
|
|
7870
|
-
const merged = [];
|
|
7871
|
-
let finalExtra = null;
|
|
7872
|
-
let finalLogPb = null;
|
|
7873
|
-
let finalStatusCode = 0;
|
|
7874
|
-
parts.forEach((segmentData, index) => {
|
|
7875
|
-
if (segmentData && Array.isArray(segmentData.danmaku_list)) {
|
|
7876
|
-
merged.push(...segmentData.danmaku_list);
|
|
7877
|
-
if (index === 0) {
|
|
7878
|
-
finalExtra = segmentData.extra;
|
|
7879
|
-
finalLogPb = segmentData.log_pb;
|
|
7880
|
-
finalStatusCode = segmentData.status_code ?? 0;
|
|
7881
|
-
}
|
|
7882
|
-
}
|
|
7883
|
-
});
|
|
7884
|
-
merged.sort((a, b) => (a.offset_time ?? 0) - (b.offset_time ?? 0));
|
|
7903
|
+
aggregate: ({ parts, head }, params) => {
|
|
7904
|
+
const merged = parts.flatMap((seg) => seg?.danmaku_list ?? []).sort((a, b) => a.offset_time - b.offset_time);
|
|
7885
7905
|
return {
|
|
7906
|
+
...head,
|
|
7886
7907
|
danmaku_list: merged,
|
|
7887
|
-
start_time:
|
|
7888
|
-
end_time:
|
|
7889
|
-
total: merged.length
|
|
7890
|
-
status_code: finalStatusCode,
|
|
7891
|
-
extra: finalExtra,
|
|
7892
|
-
log_pb: finalLogPb
|
|
7908
|
+
start_time: params.start_time ?? 0,
|
|
7909
|
+
end_time: params.end_time ?? params.duration,
|
|
7910
|
+
total: merged.length
|
|
7893
7911
|
};
|
|
7894
7912
|
},
|
|
7895
7913
|
response: type()
|
|
@@ -7901,7 +7919,7 @@ const danmakuList$1 = defineEndpoint({
|
|
|
7901
7919
|
*
|
|
7902
7920
|
* 与旧版一致:`getDynamicEmojiList` GET + a_bogus 签名。
|
|
7903
7921
|
*/
|
|
7904
|
-
const dynamicEmojiList =
|
|
7922
|
+
const dynamicEmojiList = defineDouyinEndpoint({
|
|
7905
7923
|
name: "douyin.dynamicEmojiList",
|
|
7906
7924
|
route: "/fetch_emoji_pro_list",
|
|
7907
7925
|
doc: {
|
|
@@ -7925,7 +7943,7 @@ const dynamicEmojiList = defineEndpoint({
|
|
|
7925
7943
|
*
|
|
7926
7944
|
* 与旧版一致:`getEmojiList` GET,**不带签名参数**。
|
|
7927
7945
|
*/
|
|
7928
|
-
const emojiList$3 =
|
|
7946
|
+
const emojiList$3 = defineDouyinEndpoint({
|
|
7929
7947
|
name: "douyin.emojiList",
|
|
7930
7948
|
route: "/fetch_emoji_list",
|
|
7931
7949
|
doc: {
|
|
@@ -7959,7 +7977,7 @@ const emojiList$3 = defineEndpoint({
|
|
|
7959
7977
|
* 超时是「本次调用」的属性而不是端点的属性)。
|
|
7960
7978
|
|
|
7961
7979
|
*/
|
|
7962
|
-
const emojiResourceMeta =
|
|
7980
|
+
const emojiResourceMeta = defineDouyinEndpoint({
|
|
7963
7981
|
name: "douyin.emojiResourceMeta",
|
|
7964
7982
|
route: "/fetch_emoji_resource_meta",
|
|
7965
7983
|
doc: {
|
|
@@ -7994,7 +8012,7 @@ const emojiResourceMeta = defineEndpoint({
|
|
|
7994
8012
|
* 接口的 `has_more` 形状还没有样本可依。
|
|
7995
8013
|
|
|
7996
8014
|
*/
|
|
7997
|
-
const guestMusicAwemeList =
|
|
8015
|
+
const guestMusicAwemeList = defineDouyinEndpoint({
|
|
7998
8016
|
name: "douyin.guestMusicAwemeList",
|
|
7999
8017
|
route: "/fetch_guest_music_aweme_list",
|
|
8000
8018
|
doc: {
|
|
@@ -8025,7 +8043,7 @@ const guestMusicAwemeList = defineEndpoint({
|
|
|
8025
8043
|
* `extra.extract_item_id` 就是创建这条原声的那个作品。哪条优先由调用方按场景排。
|
|
8026
8044
|
|
|
8027
8045
|
*/
|
|
8028
|
-
const guestMusicInfo =
|
|
8046
|
+
const guestMusicInfo = defineDouyinEndpoint({
|
|
8029
8047
|
name: "douyin.guestMusicInfo",
|
|
8030
8048
|
route: "/fetch_guest_music_info",
|
|
8031
8049
|
doc: {
|
|
@@ -8055,7 +8073,7 @@ const guestMusicInfo = defineEndpoint({
|
|
|
8055
8073
|
* 「设备参数 × 会话」的交叉校验,这条接口本来不需要身份。
|
|
8056
8074
|
|
|
8057
8075
|
*/
|
|
8058
|
-
const guestUserInfo =
|
|
8076
|
+
const guestUserInfo = defineDouyinEndpoint({
|
|
8059
8077
|
name: "douyin.guestUserInfo",
|
|
8060
8078
|
route: "/fetch_guest_user_info",
|
|
8061
8079
|
doc: {
|
|
@@ -8078,7 +8096,7 @@ const guestUserInfo = defineEndpoint({
|
|
|
8078
8096
|
*
|
|
8079
8097
|
* 行为与旧版一致:`getWorkDetail` GET + a_bogus 签名。
|
|
8080
8098
|
*/
|
|
8081
|
-
const imageAlbumWork =
|
|
8099
|
+
const imageAlbumWork = defineDouyinEndpoint({
|
|
8082
8100
|
name: "douyin.imageAlbumWork",
|
|
8083
8101
|
route: "/fetch_image_album_work",
|
|
8084
8102
|
doc: {
|
|
@@ -8137,7 +8155,7 @@ const withDouyinReferer = (ctx, page) => {
|
|
|
8137
8155
|
* 与旧版一致:`getLiveRoomInfo` GET + a_bogus 签名,
|
|
8138
8156
|
* Referer 指向 `https://live.douyin.com/{web_rid}`。
|
|
8139
8157
|
*/
|
|
8140
|
-
const liveRoomInfo$1 =
|
|
8158
|
+
const liveRoomInfo$1 = defineDouyinEndpoint({
|
|
8141
8159
|
name: "douyin.liveRoomInfo",
|
|
8142
8160
|
route: "/fetch_user_live_videos",
|
|
8143
8161
|
doc: {
|
|
@@ -8172,7 +8190,7 @@ const liveRoomInfo$1 = defineEndpoint({
|
|
|
8172
8190
|
* 映射条目 `DyLoginQrcode` 是本端点的**原始响应**,与 `DyPassportQrcode`
|
|
8173
8191
|
* (登录状态机归一化后的形状,runtime/session 用)不是一回事。
|
|
8174
8192
|
*/
|
|
8175
|
-
const loginQrcode =
|
|
8193
|
+
const loginQrcode = defineDouyinEndpoint({
|
|
8176
8194
|
name: "douyin.loginQrcode",
|
|
8177
8195
|
route: "/fetch_login_qrcode",
|
|
8178
8196
|
doc: {
|
|
@@ -8196,7 +8214,7 @@ const loginQrcode = defineEndpoint({
|
|
|
8196
8214
|
*
|
|
8197
8215
|
* 与旧版一致:`getMusicInfo` GET + a_bogus 签名。
|
|
8198
8216
|
*/
|
|
8199
|
-
const musicInfo =
|
|
8217
|
+
const musicInfo = defineDouyinEndpoint({
|
|
8200
8218
|
name: "douyin.musicInfo",
|
|
8201
8219
|
route: "/fetch_music_work",
|
|
8202
8220
|
doc: {
|
|
@@ -8222,7 +8240,7 @@ const musicInfo = defineEndpoint({
|
|
|
8222
8240
|
* 其余 4 个各占新路径。
|
|
8223
8241
|
* 与旧版一致:`getWorkDetail` GET + a_bogus 签名,返回原始响应。
|
|
8224
8242
|
*/
|
|
8225
|
-
const parseWork =
|
|
8243
|
+
const parseWork = defineDouyinEndpoint({
|
|
8226
8244
|
name: "douyin.parseWork",
|
|
8227
8245
|
route: "/fetch_one_work",
|
|
8228
8246
|
doc: {
|
|
@@ -8366,7 +8384,7 @@ const SEARCH_TYPE_FIELD = "__search_type";
|
|
|
8366
8384
|
*
|
|
8367
8385
|
* 与旧版一致:**不签名**。
|
|
8368
8386
|
*/
|
|
8369
|
-
const search =
|
|
8387
|
+
const search = defineDouyinEndpoint({
|
|
8370
8388
|
name: "douyin.search",
|
|
8371
8389
|
route: "/fetch_search_info",
|
|
8372
8390
|
doc: {
|
|
@@ -8423,50 +8441,47 @@ const search = defineEndpoint({
|
|
|
8423
8441
|
judge: searchJudge,
|
|
8424
8442
|
paginate: {
|
|
8425
8443
|
maxPageSize: 15,
|
|
8444
|
+
page: type(),
|
|
8426
8445
|
items: (page) => {
|
|
8427
|
-
|
|
8428
|
-
|
|
8429
|
-
return p.data ?? [];
|
|
8446
|
+
if (Array.isArray(page.user_list)) return page.user_list;
|
|
8447
|
+
return page.data ?? [];
|
|
8430
8448
|
},
|
|
8431
8449
|
hasMore: (page) => page.has_more !== 0,
|
|
8432
8450
|
nextParams: (params, page) => {
|
|
8433
8451
|
const p = page;
|
|
8434
8452
|
const nextSearchId = (typeof p.rid === "string" && p.rid.length > 0 ? p.rid : void 0) ?? (typeof p.log_pb?.impr_id === "string" ? p.log_pb.impr_id : void 0) ?? params.search_id;
|
|
8435
8453
|
const currentOffset = params.offset ?? 0;
|
|
8436
|
-
const items = Array.isArray(page
|
|
8454
|
+
const items = Array.isArray(page.user_list) ? page.user_list : page.data ?? [];
|
|
8437
8455
|
const nextOffset = typeof p.cursor === "number" && p.cursor > currentOffset ? p.cursor : currentOffset + items.length;
|
|
8438
8456
|
return {
|
|
8439
8457
|
...params,
|
|
8440
8458
|
search_id: nextSearchId,
|
|
8441
8459
|
offset: nextOffset
|
|
8442
8460
|
};
|
|
8461
|
+
},
|
|
8462
|
+
merge: ({ lastPage, items }, params) => {
|
|
8463
|
+
/**
|
|
8464
|
+
* 判别值 = **响应形状** 与 **请求类型** 的组合,各管一半:
|
|
8465
|
+
*
|
|
8466
|
+
* - **user 只看结构**:成功路径上 user 必有 `user_list`、video / general 必有 `data`
|
|
8467
|
+
* (`searchJudge` 就是按这条判反爬的),所以 `user_list` 在不在是可靠信号。
|
|
8468
|
+
* - **general 与 video 之间没有可靠结构信号**,只能取 `params.type`。
|
|
8469
|
+
* **反爬包装的形态跟逻辑类型不是一回事,不能当判据** —— 分页最后一页是单个
|
|
8470
|
+
* 合法 JSON,会被解析成对象,按 wire body 形态判会把 general 误标成 video。
|
|
8471
|
+
* - 两者矛盾时(请求 user、回的却是 data 形状)按 `general` 记:「不是 user」比
|
|
8472
|
+
* 「请求说是 user」更硬。
|
|
8473
|
+
*/
|
|
8474
|
+
const requested = params.type ?? "general";
|
|
8475
|
+
const searchType = Array.isArray(lastPage.user_list) ? "user" : requested === "user" ? "general" : requested;
|
|
8476
|
+
const out = {
|
|
8477
|
+
...lastPage,
|
|
8478
|
+
[SEARCH_TYPE_FIELD]: searchType
|
|
8479
|
+
};
|
|
8480
|
+
if (Array.isArray(lastPage.user_list)) out.user_list = items;
|
|
8481
|
+
else out.data = items;
|
|
8482
|
+
return out;
|
|
8443
8483
|
}
|
|
8444
8484
|
},
|
|
8445
|
-
normalize: (decoded, params) => {
|
|
8446
|
-
const { lastPage, items } = decoded;
|
|
8447
|
-
const page = lastPage;
|
|
8448
|
-
/**
|
|
8449
|
-
* 判别值 = **响应形状** 与 **请求类型** 的组合,各管一半:
|
|
8450
|
-
*
|
|
8451
|
-
* - **user 只看结构**:成功路径上 user 必有 `user_list`、video / general 必有 `data`
|
|
8452
|
-
* (`searchJudge` 就是按这条判反爬的),所以 `user_list` 在不在是可靠信号。
|
|
8453
|
-
* - **general 与 video 之间没有可靠结构信号**,只能取 `params.type`。
|
|
8454
|
-
* **反爬包装的形态跟逻辑类型不是一回事,不能当判据** —— 分页最后一页是单个
|
|
8455
|
-
* 合法 JSON,会被解析成对象,按 wire body 形态判会把 general 误标成 video。
|
|
8456
|
-
* - 两者矛盾时(请求 user、回的却是 data 形状)按 `general` 记:「不是 user」比
|
|
8457
|
-
* 「请求说是 user」更硬 —— 记成 user 会让下游收窄到「有 `user_list` 的那一支」,
|
|
8458
|
-
* 而那恰恰是它没有的东西。
|
|
8459
|
-
*/
|
|
8460
|
-
const requested = params.type ?? "general";
|
|
8461
|
-
const searchType = Array.isArray(page?.user_list) ? "user" : requested === "user" ? "general" : requested;
|
|
8462
|
-
const out = {
|
|
8463
|
-
...page ?? {},
|
|
8464
|
-
[SEARCH_TYPE_FIELD]: searchType
|
|
8465
|
-
};
|
|
8466
|
-
if (Array.isArray(page?.user_list)) out.user_list = items;
|
|
8467
|
-
else out.data = items;
|
|
8468
|
-
return out;
|
|
8469
|
-
},
|
|
8470
8485
|
response: type()
|
|
8471
8486
|
});
|
|
8472
8487
|
//#endregion
|
|
@@ -8476,7 +8491,7 @@ const search = defineEndpoint({
|
|
|
8476
8491
|
*
|
|
8477
8492
|
* 行为与旧版一致:`getWorkDetail` GET + a_bogus 签名。
|
|
8478
8493
|
*/
|
|
8479
|
-
const slidesWork =
|
|
8494
|
+
const slidesWork = defineDouyinEndpoint({
|
|
8480
8495
|
name: "douyin.slidesWork",
|
|
8481
8496
|
route: "/fetch_slides_work",
|
|
8482
8497
|
doc: {
|
|
@@ -8501,7 +8516,7 @@ const slidesWork = defineEndpoint({
|
|
|
8501
8516
|
* 与旧版一致:`getSuggestWords` GET + a_bogus 签名,
|
|
8502
8517
|
* Referer 指向 `https://www.douyin.com/search/{query}`。
|
|
8503
8518
|
*/
|
|
8504
|
-
const suggestWords =
|
|
8519
|
+
const suggestWords = defineDouyinEndpoint({
|
|
8505
8520
|
name: "douyin.suggestWords",
|
|
8506
8521
|
route: "/fetch_suggest_words",
|
|
8507
8522
|
doc: {
|
|
@@ -8529,7 +8544,7 @@ const suggestWords = defineEndpoint({
|
|
|
8529
8544
|
*
|
|
8530
8545
|
* 行为与旧版一致:`getWorkDetail` GET + a_bogus 签名。
|
|
8531
8546
|
*/
|
|
8532
|
-
const textWork =
|
|
8547
|
+
const textWork = defineDouyinEndpoint({
|
|
8533
8548
|
name: "douyin.textWork",
|
|
8534
8549
|
route: "/fetch_text_work",
|
|
8535
8550
|
doc: {
|
|
@@ -8554,7 +8569,7 @@ const textWork = defineEndpoint({
|
|
|
8554
8569
|
* 与旧版一致:`getUserFavoriteList` GET + a_bogus 签名,
|
|
8555
8570
|
* 游标是 `max_cursor`(`has_more === 1` 继续),Referer 指向用户主页。
|
|
8556
8571
|
*/
|
|
8557
|
-
const userFavoriteList =
|
|
8572
|
+
const userFavoriteList = defineDouyinEndpoint({
|
|
8558
8573
|
name: "douyin.userFavoriteList",
|
|
8559
8574
|
route: "/fetch_user_favorite_list",
|
|
8560
8575
|
doc: {
|
|
@@ -8584,14 +8599,11 @@ const userFavoriteList = defineEndpoint({
|
|
|
8584
8599
|
nextParams: (params, page) => ({
|
|
8585
8600
|
...params,
|
|
8586
8601
|
max_cursor: page.max_cursor?.toString() ?? "0"
|
|
8587
|
-
})
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
const { lastPage, items } = decoded;
|
|
8591
|
-
return {
|
|
8592
|
-
...lastPage ?? {},
|
|
8602
|
+
}),
|
|
8603
|
+
merge: ({ lastPage, items }) => ({
|
|
8604
|
+
...lastPage,
|
|
8593
8605
|
aweme_list: items
|
|
8594
|
-
}
|
|
8606
|
+
})
|
|
8595
8607
|
},
|
|
8596
8608
|
response: type()
|
|
8597
8609
|
});
|
|
@@ -8603,7 +8615,7 @@ const userFavoriteList = defineEndpoint({
|
|
|
8603
8615
|
* 与旧版一致:`getUserProfile` GET + a_bogus 签名,
|
|
8604
8616
|
* Referer 指向 `https://www.douyin.com/user/{sec_uid}`(由 {@link withDouyinReferer} 注入)。
|
|
8605
8617
|
*/
|
|
8606
|
-
const userProfile$3 =
|
|
8618
|
+
const userProfile$3 = defineDouyinEndpoint({
|
|
8607
8619
|
name: "douyin.userProfile",
|
|
8608
8620
|
route: "/fetch_user_info",
|
|
8609
8621
|
doc: {
|
|
@@ -8633,7 +8645,7 @@ const userProfile$3 = defineEndpoint({
|
|
|
8633
8645
|
* 签名,游标是 `max_cursor`。**`hasMore` 判的是 `has_more === true`(布尔)**,
|
|
8634
8646
|
* 与 userVideoList / userFavoriteList 的 `=== 1` 不同。
|
|
8635
8647
|
*/
|
|
8636
|
-
const userRecommendList =
|
|
8648
|
+
const userRecommendList = defineDouyinEndpoint({
|
|
8637
8649
|
name: "douyin.userRecommendList",
|
|
8638
8650
|
route: "/fetch_user_recommend_list",
|
|
8639
8651
|
doc: {
|
|
@@ -8663,14 +8675,11 @@ const userRecommendList = defineEndpoint({
|
|
|
8663
8675
|
nextParams: (params, page) => ({
|
|
8664
8676
|
...params,
|
|
8665
8677
|
max_cursor: page.max_cursor?.toString() ?? "0"
|
|
8666
|
-
})
|
|
8667
|
-
|
|
8668
|
-
|
|
8669
|
-
const { lastPage, items } = decoded;
|
|
8670
|
-
return {
|
|
8671
|
-
...lastPage ?? {},
|
|
8678
|
+
}),
|
|
8679
|
+
merge: ({ lastPage, items }) => ({
|
|
8680
|
+
...lastPage,
|
|
8672
8681
|
aweme_list: items
|
|
8673
|
-
}
|
|
8682
|
+
})
|
|
8674
8683
|
},
|
|
8675
8684
|
response: type()
|
|
8676
8685
|
});
|
|
@@ -8683,7 +8692,7 @@ const userRecommendList = defineEndpoint({
|
|
|
8683
8692
|
* 游标是 `max_cursor`(字符串,`has_more === 1` 继续),Referer 指向用户主页。
|
|
8684
8693
|
* 最终形状 `{ ...最后一页, aweme_list }`。
|
|
8685
8694
|
*/
|
|
8686
|
-
const userVideoList =
|
|
8695
|
+
const userVideoList = defineDouyinEndpoint({
|
|
8687
8696
|
name: "douyin.userVideoList",
|
|
8688
8697
|
route: "/fetch_user_post_videos",
|
|
8689
8698
|
doc: {
|
|
@@ -8713,14 +8722,11 @@ const userVideoList = defineEndpoint({
|
|
|
8713
8722
|
nextParams: (params, page) => ({
|
|
8714
8723
|
...params,
|
|
8715
8724
|
max_cursor: page.max_cursor?.toString() ?? "0"
|
|
8716
|
-
})
|
|
8717
|
-
|
|
8718
|
-
|
|
8719
|
-
const { lastPage, items } = decoded;
|
|
8720
|
-
return {
|
|
8721
|
-
...lastPage ?? {},
|
|
8725
|
+
}),
|
|
8726
|
+
merge: ({ lastPage, items }) => ({
|
|
8727
|
+
...lastPage,
|
|
8722
8728
|
aweme_list: items
|
|
8723
|
-
}
|
|
8729
|
+
})
|
|
8724
8730
|
},
|
|
8725
8731
|
response: type()
|
|
8726
8732
|
});
|
|
@@ -8737,7 +8743,7 @@ const userVideoList = defineEndpoint({
|
|
|
8737
8743
|
*/
|
|
8738
8744
|
const douyinRegistry = {
|
|
8739
8745
|
parseWork,
|
|
8740
|
-
videoWork:
|
|
8746
|
+
videoWork: defineDouyinEndpoint({
|
|
8741
8747
|
name: "douyin.videoWork",
|
|
8742
8748
|
route: "/fetch_video_work",
|
|
8743
8749
|
doc: {
|
|
@@ -10985,6 +10991,18 @@ const kuaishouDidState = createKuaishouDidState();
|
|
|
10985
10991
|
*/
|
|
10986
10992
|
const kuaishouDidPrepare = async (ctx) => ({ cookie: buildKuaishouDidCookie(kuaishouDidState.getDid(), ctx.cookie) });
|
|
10987
10993
|
//#endregion
|
|
10994
|
+
//#region src/platforms/kuaishou/endpoints/define.ts
|
|
10995
|
+
/**
|
|
10996
|
+
* 快手端点声明。
|
|
10997
|
+
*
|
|
10998
|
+
* 就是 {@link defineEndpoint} 绑定了快手签名器名联合 {@link KuaishouSignerName}:
|
|
10999
|
+
* `sign` 的字符串分支被收窄成 `'hxfalcon'`,写错名字编译期即报错,不必等运行时
|
|
11000
|
+
* 查表失败。运行时仍是恒等函数,其余行为与通用 `defineEndpoint` 完全一致。
|
|
11001
|
+
* @param def - 端点声明(`sign` 的名字受快手签名器表约束)
|
|
11002
|
+
* @returns 原样返回 `def`,带上推导好的具体类型
|
|
11003
|
+
*/
|
|
11004
|
+
const defineKuaishouEndpoint = (def) => defineEndpoint(def);
|
|
11005
|
+
//#endregion
|
|
10988
11006
|
//#region src/platforms/kuaishou/endpoints/comments.ts
|
|
10989
11007
|
/**
|
|
10990
11008
|
* 获取作品评论(H5 `photo/comment/list`,POST + 声明式翻页)。
|
|
@@ -11004,7 +11022,7 @@ const kuaishouDidPrepare = async (ctx) => ({ cookie: buildKuaishouDidCookie(kuai
|
|
|
11004
11022
|
* 翻页:调用方传 `number` 指定目标条数,`pcursor` 由 `paginate` 声明管理,
|
|
11005
11023
|
* 不暴露为自由参数。
|
|
11006
11024
|
*/
|
|
11007
|
-
const comments =
|
|
11025
|
+
const comments = defineKuaishouEndpoint({
|
|
11008
11026
|
name: "kuaishou.comments",
|
|
11009
11027
|
route: "/fetch_work_comments",
|
|
11010
11028
|
doc: {
|
|
@@ -11037,14 +11055,11 @@ const comments = defineEndpoint({
|
|
|
11037
11055
|
nextParams: (params, page) => ({
|
|
11038
11056
|
...params,
|
|
11039
11057
|
pcursor: page.pcursor ?? ""
|
|
11040
|
-
})
|
|
11041
|
-
|
|
11042
|
-
|
|
11043
|
-
const { lastPage, items } = decoded;
|
|
11044
|
-
return {
|
|
11045
|
-
...lastPage ?? {},
|
|
11058
|
+
}),
|
|
11059
|
+
merge: ({ lastPage, items }) => ({
|
|
11060
|
+
...lastPage,
|
|
11046
11061
|
rootComments: items
|
|
11047
|
-
}
|
|
11062
|
+
})
|
|
11048
11063
|
},
|
|
11049
11064
|
response: type()
|
|
11050
11065
|
});
|
|
@@ -11120,7 +11135,7 @@ const resolveScanRange = (p) => {
|
|
|
11120
11135
|
* 同许可):https://github.com/OduckO —— `src/platform/kuaishou/danmaku.ts` 与
|
|
11121
11136
|
* `TODO.md:140-164`。
|
|
11122
11137
|
*/
|
|
11123
|
-
const danmakuList =
|
|
11138
|
+
const danmakuList = defineKuaishouEndpoint({
|
|
11124
11139
|
name: "kuaishou.danmakuList",
|
|
11125
11140
|
route: "/fetch_danmaku_list",
|
|
11126
11141
|
doc: {
|
|
@@ -11182,39 +11197,38 @@ const danmakuList = defineEndpoint({
|
|
|
11182
11197
|
retryOn: ["PLATFORM_UNAVAILABLE"],
|
|
11183
11198
|
partial: "tolerate",
|
|
11184
11199
|
/**
|
|
11185
|
-
* 合并多个窗口:去重 → 按时间升序 →
|
|
11200
|
+
* 合并多个窗口:去重 → 按时间升序 → 写回整体扫描范围。
|
|
11201
|
+
*
|
|
11202
|
+
* 这不是「归一化」,只是把分段拿到的条目放回原位(与 `comments` 的跨页累积同语义),
|
|
11203
|
+
* 所以返回类型仍描述真实 GraphQL 形状;只有 `positionFromInclude` / `positionToExclude`
|
|
11204
|
+
* 被改写成本次**整体**扫描范围(单窗口时就等于那一窗的值)。
|
|
11186
11205
|
*
|
|
11187
|
-
*
|
|
11188
|
-
*
|
|
11189
|
-
* `
|
|
11190
|
-
* 单窗口调用时它们就等于那一窗的值。
|
|
11191
|
-
* @param decoded - 每个窗口 decode 之后的值(失败窗口是 `undefined`)
|
|
11206
|
+
* 走 typed 的 aggregate(而非 normalize):`parts` 已收窄成 `Array<…Response | undefined>`、
|
|
11207
|
+
* `head` 恒有值,去重 / 排序 / `...head` 全程无断言。
|
|
11208
|
+
* @param value - 各窗口累积值(`parts` 含失败窗口的 undefined,`head` 恒有值)
|
|
11192
11209
|
* @param params - 校验后的参数
|
|
11193
11210
|
* @returns 合并后的响应
|
|
11194
11211
|
*/
|
|
11195
|
-
|
|
11196
|
-
const windows = decoded;
|
|
11212
|
+
aggregate: ({ parts, head }, params) => {
|
|
11197
11213
|
const { from, to } = resolveScanRange(params);
|
|
11198
11214
|
const merged = [];
|
|
11199
11215
|
const seen = /* @__PURE__ */ new Set();
|
|
11200
|
-
for (const win of
|
|
11216
|
+
for (const win of parts) {
|
|
11201
11217
|
const node = win?.data?.visionDanmaku;
|
|
11202
11218
|
if (!node) continue;
|
|
11203
|
-
for (const row of node.danmakus
|
|
11204
|
-
|
|
11205
|
-
|
|
11206
|
-
seen.add(key);
|
|
11219
|
+
for (const row of node.danmakus) {
|
|
11220
|
+
if (seen.has(row.id)) continue;
|
|
11221
|
+
seen.add(row.id);
|
|
11207
11222
|
merged.push(row);
|
|
11208
11223
|
}
|
|
11209
11224
|
}
|
|
11210
|
-
merged.sort((a, b) =>
|
|
11211
|
-
const firstBody = windows.find((win) => Boolean(win?.data?.visionDanmaku));
|
|
11225
|
+
merged.sort((a, b) => a.position - b.position);
|
|
11212
11226
|
return {
|
|
11213
|
-
...
|
|
11227
|
+
...head,
|
|
11214
11228
|
data: {
|
|
11215
|
-
...
|
|
11229
|
+
...head.data,
|
|
11216
11230
|
visionDanmaku: {
|
|
11217
|
-
...
|
|
11231
|
+
...head.data.visionDanmaku,
|
|
11218
11232
|
positionFromInclude: from,
|
|
11219
11233
|
positionToExclude: to,
|
|
11220
11234
|
danmakus: merged
|
|
@@ -11236,7 +11250,7 @@ const danmakuList = defineEndpoint({
|
|
|
11236
11250
|
* 也正因为它零凭证,它是「无 cookie 能不能取到数」的**基线探针** ——
|
|
11237
11251
|
* 这条通而 videoWork 不通,问题就在签名或 did,不在网络环境。
|
|
11238
11252
|
*/
|
|
11239
|
-
const emojiList$2 =
|
|
11253
|
+
const emojiList$2 = defineKuaishouEndpoint({
|
|
11240
11254
|
name: "kuaishou.emojiList",
|
|
11241
11255
|
route: "/fetch_emoji_list",
|
|
11242
11256
|
doc: {
|
|
@@ -11267,7 +11281,7 @@ const emojiList$2 = defineEndpoint({
|
|
|
11267
11281
|
* 返回结构直接包含 `liveStream` / `author` / `gameInfo` / `noticeList` 等。
|
|
11268
11282
|
* 响应判定用快手 judge(`result=1` 才进 data)。
|
|
11269
11283
|
*/
|
|
11270
|
-
const liveRoomInfo =
|
|
11284
|
+
const liveRoomInfo = defineKuaishouEndpoint({
|
|
11271
11285
|
name: "kuaishou.liveRoomInfo",
|
|
11272
11286
|
route: "/fetch_live_room_info",
|
|
11273
11287
|
doc: {
|
|
@@ -11517,7 +11531,7 @@ const mapLiveDetailToUserProfileLiveInfo = (detailData, author) => {
|
|
|
11517
11531
|
* 到空值 —— 这正是「部分失败」语义:build 返回 12 个请求并发发出,
|
|
11518
11532
|
* 失败分片在 normalize 里留空,**全部分片都失败时仍返回失败信封**。
|
|
11519
11533
|
*/
|
|
11520
|
-
const userProfile$2 =
|
|
11534
|
+
const userProfile$2 = defineKuaishouEndpoint({
|
|
11521
11535
|
name: "kuaishou.userProfile",
|
|
11522
11536
|
route: "/fetch_user_profile",
|
|
11523
11537
|
doc: {
|
|
@@ -11617,7 +11631,7 @@ const userProfile$2 = defineEndpoint({
|
|
|
11617
11631
|
* `profilePublic` 的查询参数(`userWorkList` 是 `profilePublic` 的
|
|
11618
11632
|
* 领域化封装)。
|
|
11619
11633
|
*/
|
|
11620
|
-
const userWorkList =
|
|
11634
|
+
const userWorkList = defineKuaishouEndpoint({
|
|
11621
11635
|
name: "kuaishou.userWorkList",
|
|
11622
11636
|
route: "/fetch_user_work_list",
|
|
11623
11637
|
doc: {
|
|
@@ -11649,19 +11663,17 @@ const userWorkList = defineEndpoint({
|
|
|
11649
11663
|
nextParams: (params, page) => ({
|
|
11650
11664
|
...params,
|
|
11651
11665
|
pcursor: page.data?.pcursor ?? ""
|
|
11652
|
-
})
|
|
11653
|
-
|
|
11654
|
-
|
|
11655
|
-
|
|
11656
|
-
|
|
11657
|
-
|
|
11658
|
-
|
|
11659
|
-
|
|
11660
|
-
|
|
11661
|
-
|
|
11662
|
-
|
|
11663
|
-
result: page?.data?.result ?? 1
|
|
11664
|
-
};
|
|
11666
|
+
}),
|
|
11667
|
+
merge: ({ lastPage, items }, params) => {
|
|
11668
|
+
const pcursor = lastPage.data?.pcursor;
|
|
11669
|
+
return {
|
|
11670
|
+
principalId: params.principalId,
|
|
11671
|
+
list: items,
|
|
11672
|
+
pcursor: typeof pcursor === "string" ? pcursor : "",
|
|
11673
|
+
hasMore: typeof pcursor === "string" && pcursor.length > 0,
|
|
11674
|
+
result: lastPage.data?.result ?? 1
|
|
11675
|
+
};
|
|
11676
|
+
}
|
|
11665
11677
|
},
|
|
11666
11678
|
response: type()
|
|
11667
11679
|
});
|
|
@@ -11682,7 +11694,7 @@ const userWorkList = defineEndpoint({
|
|
|
11682
11694
|
* 第 8 条 `/fetch_danmaku_list` 是弹幕。它**完全免鉴权**(不签名、不要 cookie)。
|
|
11683
11695
|
*/
|
|
11684
11696
|
const kuaishouRegistry = {
|
|
11685
|
-
videoWork:
|
|
11697
|
+
videoWork: defineKuaishouEndpoint({
|
|
11686
11698
|
name: "kuaishou.videoWork",
|
|
11687
11699
|
route: "/fetch_one_work",
|
|
11688
11700
|
doc: {
|
|
@@ -11702,7 +11714,7 @@ const kuaishouRegistry = {
|
|
|
11702
11714
|
},
|
|
11703
11715
|
response: type()
|
|
11704
11716
|
}),
|
|
11705
|
-
videoWorkFull:
|
|
11717
|
+
videoWorkFull: defineKuaishouEndpoint({
|
|
11706
11718
|
name: "kuaishou.videoWorkFull",
|
|
11707
11719
|
route: "/fetch_one_work_full",
|
|
11708
11720
|
doc: {
|
|
@@ -12206,6 +12218,18 @@ const xiaohongshuApiUrls$1 = {
|
|
|
12206
12218
|
searchNotes: searchNotes$1
|
|
12207
12219
|
};
|
|
12208
12220
|
//#endregion
|
|
12221
|
+
//#region src/platforms/xiaohongshu/endpoints/define.ts
|
|
12222
|
+
/**
|
|
12223
|
+
* 小红书端点声明。
|
|
12224
|
+
*
|
|
12225
|
+
* 就是 {@link defineEndpoint} 绑定了小红书签名器名联合 {@link XiaohongshuSignerName}:
|
|
12226
|
+
* `sign` 的字符串分支被收窄成小红书那几个 `xhs-*` 名字,写错名字编译期即报错,不必
|
|
12227
|
+
* 等运行时查表失败。运行时仍是恒等函数,其余行为与通用 `defineEndpoint` 完全一致。
|
|
12228
|
+
* @param def - 端点声明(`sign` 的名字受小红书签名器表约束)
|
|
12229
|
+
* @returns 原样返回 `def`,带上推导好的具体类型
|
|
12230
|
+
*/
|
|
12231
|
+
const defineXiaohongshuEndpoint = (def) => defineEndpoint(def);
|
|
12232
|
+
//#endregion
|
|
12209
12233
|
//#region src/platforms/xiaohongshu/endpoints/emojiList.ts
|
|
12210
12234
|
/**
|
|
12211
12235
|
* 表情列表(GET,无参数)。
|
|
@@ -12213,7 +12237,7 @@ const xiaohongshuApiUrls$1 = {
|
|
|
12213
12237
|
* 没有业务参数 —— 用空对象 schema(`zod.object({})`),
|
|
12214
12238
|
* fetcher 方法可以不传 options。
|
|
12215
12239
|
*/
|
|
12216
|
-
const emojiList =
|
|
12240
|
+
const emojiList = defineXiaohongshuEndpoint({
|
|
12217
12241
|
name: "xiaohongshu.emojiList",
|
|
12218
12242
|
route: "/fetch_emoji_list",
|
|
12219
12243
|
doc: {
|
|
@@ -12244,7 +12268,7 @@ const emojiList = defineEndpoint({
|
|
|
12244
12268
|
* 进 trace)。已有 a1 直接跳过 —— guest cookie 流程有真实网络开销,
|
|
12245
12269
|
* 不能每次调用都重跑。
|
|
12246
12270
|
*/
|
|
12247
|
-
const homeFeed =
|
|
12271
|
+
const homeFeed = defineXiaohongshuEndpoint({
|
|
12248
12272
|
name: "xiaohongshu.homeFeed",
|
|
12249
12273
|
route: "/fetch_home_feed",
|
|
12250
12274
|
doc: {
|
|
@@ -12287,7 +12311,7 @@ const homeFeed = defineEndpoint({
|
|
|
12287
12311
|
* `data.has_more` 是 boolean,与抖音的 `has_more === 1` 不同 ——
|
|
12288
12312
|
* 差异收敛在 `paginate` 声明里,管线不感知。
|
|
12289
12313
|
*/
|
|
12290
|
-
const noteComments =
|
|
12314
|
+
const noteComments = defineXiaohongshuEndpoint({
|
|
12291
12315
|
name: "xiaohongshu.noteComments",
|
|
12292
12316
|
route: "/fetch_note_comments",
|
|
12293
12317
|
doc: {
|
|
@@ -12316,18 +12340,14 @@ const noteComments = defineEndpoint({
|
|
|
12316
12340
|
nextParams: (params, page) => ({
|
|
12317
12341
|
...params,
|
|
12318
12342
|
cursor: page.data?.cursor ?? ""
|
|
12319
|
-
})
|
|
12320
|
-
|
|
12321
|
-
|
|
12322
|
-
const { lastPage, items } = decoded;
|
|
12323
|
-
const page = lastPage;
|
|
12324
|
-
return {
|
|
12325
|
-
...page ?? {},
|
|
12343
|
+
}),
|
|
12344
|
+
merge: ({ lastPage, items }) => ({
|
|
12345
|
+
...lastPage,
|
|
12326
12346
|
data: {
|
|
12327
|
-
...
|
|
12347
|
+
...lastPage.data,
|
|
12328
12348
|
comments: items
|
|
12329
12349
|
}
|
|
12330
|
-
}
|
|
12350
|
+
})
|
|
12331
12351
|
},
|
|
12332
12352
|
response: type()
|
|
12333
12353
|
});
|
|
@@ -12338,7 +12358,7 @@ const noteComments = defineEndpoint({
|
|
|
12338
12358
|
*
|
|
12339
12359
|
* `note_id` 与 `xsec_token` 都要求 `min(1)`:空字符串会发出一个必败的请求。
|
|
12340
12360
|
*/
|
|
12341
|
-
const noteDetail =
|
|
12361
|
+
const noteDetail = defineXiaohongshuEndpoint({
|
|
12342
12362
|
name: "xiaohongshu.noteDetail",
|
|
12343
12363
|
route: "/fetch_one_note",
|
|
12344
12364
|
doc: {
|
|
@@ -12369,7 +12389,7 @@ const noteDetail = defineEndpoint({
|
|
|
12369
12389
|
* `search_id` 是每次搜索的随机 id:在 `build` 里显式生成并传给
|
|
12370
12390
|
* `api.searchNotes(data, searchId)`(api 保持纯函数,随机源归 `sign/`)。
|
|
12371
12391
|
*/
|
|
12372
|
-
const searchNotes =
|
|
12392
|
+
const searchNotes = defineXiaohongshuEndpoint({
|
|
12373
12393
|
name: "xiaohongshu.searchNotes",
|
|
12374
12394
|
route: "/fetch_search_notes",
|
|
12375
12395
|
doc: {
|
|
@@ -12402,7 +12422,7 @@ const searchNotes = defineEndpoint({
|
|
|
12402
12422
|
* XYS_ 签名会被平台以 HTTP 406 拒绝,因此用 `'xhs-get-xyw-trace'`
|
|
12403
12423
|
* (XYW_ GET 签名 + traceid),而不是给所有 GET 都换。
|
|
12404
12424
|
*/
|
|
12405
|
-
const userNoteList =
|
|
12425
|
+
const userNoteList = defineXiaohongshuEndpoint({
|
|
12406
12426
|
name: "xiaohongshu.userNoteList",
|
|
12407
12427
|
route: "/fetch_user_notes",
|
|
12408
12428
|
doc: {
|
|
@@ -12441,7 +12461,7 @@ const xiaohongshuRegistry = {
|
|
|
12441
12461
|
homeFeed,
|
|
12442
12462
|
noteDetail,
|
|
12443
12463
|
noteComments,
|
|
12444
|
-
userProfile:
|
|
12464
|
+
userProfile: defineXiaohongshuEndpoint({
|
|
12445
12465
|
name: "xiaohongshu.userProfile",
|
|
12446
12466
|
route: "/fetch_user_profile",
|
|
12447
12467
|
doc: {
|
|
@@ -54020,7 +54040,7 @@ const buildOpenApiSpec = (options = {}) => {
|
|
|
54020
54040
|
openapi: "3.1.0",
|
|
54021
54041
|
info: {
|
|
54022
54042
|
title: "amagi HTTP API",
|
|
54023
|
-
version: options.version ?? "7.0.0-beta.
|
|
54043
|
+
version: options.version ?? "7.0.0-beta.5",
|
|
54024
54044
|
description: [
|
|
54025
54045
|
"由端点注册表派生的规范 —— 手写的那份已经漂移,这份不会(`gen-openapi --check` 进 CI)。",
|
|
54026
54046
|
"",
|
|
@@ -56843,7 +56863,7 @@ let DynamicType = /* @__PURE__ */ function(DynamicType) {
|
|
|
56843
56863
|
* 构建后使用 __VERSION__,开发环境从 package.json 读取
|
|
56844
56864
|
*/
|
|
56845
56865
|
const getVersion = () => {
|
|
56846
|
-
return "7.0.0-beta.
|
|
56866
|
+
return "7.0.0-beta.5";
|
|
56847
56867
|
};
|
|
56848
56868
|
const VERSION = getVersion();
|
|
56849
56869
|
/**
|