@gengchenchina/chrome-media-publish-extension 1.0.11 → 1.0.13
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/README.md +4 -0
- package/dist/index.js +204 -136
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -318,6 +318,10 @@ type PublishStatusRef = {
|
|
|
318
318
|
| `cover` | 当前使用第一张图作为封面 | 可选自定义封面,上传到 ImageX |
|
|
319
319
|
| `content` | `Array<{ url: string }>` 图片 URL 列表 | 视频 URL 字符串 |
|
|
320
320
|
|
|
321
|
+
从 `1.0.13` 开始,视频最终提交由本包原生使用创作者中心 `create_v2` JSON 协议,在 Chrome `MAIN` 页面上下文执行。立即发布的 `item.common.timing` 为数值 `0`,定时发布为 Unix 秒数;无需宿主改写请求或包装共享 Toolkit 对象。
|
|
322
|
+
|
|
323
|
+
视频最终提交只发起一次。页面返回空正文、网络错误或超时时,不回退旧 `create` 接口、不从扩展后台重复提交;遇到结果不明确时,应先在创作者中心核对作品。每次视频发布使用独立的步骤生命周期与截止时间,超时后阻止尚未开始的后续步骤和最终投稿,并继续清理请求头规则。已发送到服务器的请求仍可能被受理,超时不代表撤回成功。
|
|
324
|
+
|
|
321
325
|
Douyin `publish()` 成功返回的 `statusRef.url` 是可推导候选:
|
|
322
326
|
|
|
323
327
|
- 图文:`https://www.douyin.com/note/<id>`
|
package/dist/index.js
CHANGED
|
@@ -434,10 +434,14 @@ function buildVideoTextPayload(media, resolvedTags = []) {
|
|
|
434
434
|
resolvedTags
|
|
435
435
|
});
|
|
436
436
|
}
|
|
437
|
-
function
|
|
437
|
+
function buildVideoPublishItem(media, video, posterUri, cover, resolvedTags = [], scheduleTime) {
|
|
438
438
|
const textPayload = buildVideoTextPayload(media, resolvedTags);
|
|
439
|
-
const timing = normalizeDouyinTiming(scheduleTime);
|
|
440
|
-
|
|
439
|
+
const timing = normalizeDouyinTiming(scheduleTime) ?? 0;
|
|
440
|
+
if (!Number.isSafeInteger(timing) || timing < 0) {
|
|
441
|
+
throw new Error("\u6296\u97F3\u5B9A\u65F6\u53D1\u5E03\u65F6\u95F4\u65E0\u6548");
|
|
442
|
+
}
|
|
443
|
+
if (!video.vid || !posterUri) throw new Error("\u6296\u97F3\u89C6\u9891\u6216\u5C01\u9762\u53C2\u6570\u65E0\u6548");
|
|
444
|
+
const common = {
|
|
441
445
|
video_id: video.vid,
|
|
442
446
|
text: textPayload.text,
|
|
443
447
|
caption: textPayload.bodyText,
|
|
@@ -447,25 +451,36 @@ function buildVideoFormPayload(media, video, posterUri, cover, resolvedTags = []
|
|
|
447
451
|
challenges: JSON.stringify(textPayload.challenges || []),
|
|
448
452
|
mentions: JSON.stringify(textPayload.mentions || []),
|
|
449
453
|
hashtag_source: textPayload.hashtagSource || "",
|
|
450
|
-
visibility_type:
|
|
451
|
-
upload_source: "1",
|
|
452
|
-
is_preview: "0",
|
|
453
|
-
is_post_assistant: "0",
|
|
454
|
-
title_permission: "1",
|
|
454
|
+
visibility_type: 0,
|
|
455
455
|
creation_id: createCreationId(),
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
if (cover
|
|
456
|
+
download: 1,
|
|
457
|
+
timing,
|
|
458
|
+
media_type: 4,
|
|
459
|
+
music_source: 0,
|
|
460
|
+
music_id: null,
|
|
461
|
+
hot_sentence: "",
|
|
462
|
+
interaction_stickers: "[]"
|
|
463
|
+
};
|
|
464
|
+
const videoCover = { poster: posterUri, poster_delay: 0 };
|
|
465
|
+
for (const [source, target] of [["width", "custom_cover_image_width"], ["height", "custom_cover_image_height"]]) {
|
|
466
|
+
if (cover?.[source] !== void 0) {
|
|
467
|
+
if (!Number.isSafeInteger(cover[source]) || cover[source] <= 0) {
|
|
468
|
+
throw new Error("\u6296\u97F3\u89C6\u9891\u5C01\u9762\u5C3A\u5BF8\u65E0\u6548");
|
|
469
|
+
}
|
|
470
|
+
videoCover[target] = cover[source];
|
|
471
|
+
}
|
|
467
472
|
}
|
|
468
|
-
return
|
|
473
|
+
return {
|
|
474
|
+
common,
|
|
475
|
+
cover: videoCover,
|
|
476
|
+
mix: {},
|
|
477
|
+
selected_member: { is_selected_member_video: false },
|
|
478
|
+
chapter: {},
|
|
479
|
+
anchor: {},
|
|
480
|
+
sync: { should_sync: false, sync_to_toutiao: 0 },
|
|
481
|
+
open_platform: {},
|
|
482
|
+
assistant: { is_preview: 0, is_post_assistant: 1 }
|
|
483
|
+
};
|
|
469
484
|
}
|
|
470
485
|
function buildTextPayloadResult({ title, text, textExtra, bodyText, resolvedTags }) {
|
|
471
486
|
const challenges = resolvedTags.map((tag) => tag.id).filter((id) => id !== void 0 && id !== null && id !== "");
|
|
@@ -522,6 +537,9 @@ async function getCreatorJson(ctx, url, headers = {}, options = {}) {
|
|
|
522
537
|
}, options);
|
|
523
538
|
}
|
|
524
539
|
async function postCreatorJson(ctx, url, body, options = {}) {
|
|
540
|
+
if (options.pageContextOnly) {
|
|
541
|
+
return postJsonFromPage(ctx, url, body, options);
|
|
542
|
+
}
|
|
525
543
|
if (options.preferPageContext) {
|
|
526
544
|
const result = await postJsonFromPage(ctx, url, body, options).catch((error) => {
|
|
527
545
|
if (isSecurityCheckError(error)) throw error;
|
|
@@ -540,25 +558,6 @@ async function postCreatorJson(ctx, url, body, options = {}) {
|
|
|
540
558
|
body: JSON.stringify(body)
|
|
541
559
|
}, options);
|
|
542
560
|
}
|
|
543
|
-
async function postCreatorForm(ctx, url, body, options = {}) {
|
|
544
|
-
if (options.preferPageContext) {
|
|
545
|
-
const result = await postFormFromPage(ctx, url, body, options).catch((error) => {
|
|
546
|
-
if (isSecurityCheckError(error)) throw error;
|
|
547
|
-
ctx.Logger?.warn?.("\u6296\u97F3\u9875\u9762\u4E0A\u4E0B\u6587 FORM POST \u5931\u8D25\uFF0C\u56DE\u9000\u6269\u5C55\u4E0A\u4E0B\u6587", error);
|
|
548
|
-
return null;
|
|
549
|
-
});
|
|
550
|
-
if (result) return result;
|
|
551
|
-
}
|
|
552
|
-
return requestJson(ctx, url, {
|
|
553
|
-
method: "POST",
|
|
554
|
-
credentials: "include",
|
|
555
|
-
headers: {
|
|
556
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
557
|
-
...options.headers
|
|
558
|
-
},
|
|
559
|
-
body: new URLSearchParams(body)
|
|
560
|
-
}, options);
|
|
561
|
-
}
|
|
562
561
|
async function getCreatorJsonWithPageFallback(ctx, url, options = {}) {
|
|
563
562
|
try {
|
|
564
563
|
return await getJsonFromPage(ctx, url, options);
|
|
@@ -587,6 +586,7 @@ async function getJsonFromPage(ctx, url, options = {}) {
|
|
|
587
586
|
ok: response.ok,
|
|
588
587
|
status: response.status,
|
|
589
588
|
statusText: response.statusText,
|
|
589
|
+
passportDecision: response.headers.get("x-tt-verify-passport-decision"),
|
|
590
590
|
text: await response.text()
|
|
591
591
|
};
|
|
592
592
|
} finally {
|
|
@@ -598,66 +598,42 @@ async function getJsonFromPage(ctx, url, options = {}) {
|
|
|
598
598
|
).then((result) => parsePageResponse(result, url)), options);
|
|
599
599
|
}
|
|
600
600
|
async function postJsonFromPage(ctx, url, body, options = {}) {
|
|
601
|
-
return withCreatorTab(ctx, async (tab) =>
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
try {
|
|
638
|
-
const response = await fetch(requestUrl, {
|
|
639
|
-
method: "POST",
|
|
640
|
-
credentials: "include",
|
|
641
|
-
headers: {
|
|
642
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
643
|
-
Accept: "application/json, text/plain, */*"
|
|
644
|
-
},
|
|
645
|
-
body: new URLSearchParams(requestBody),
|
|
646
|
-
signal: controller.signal
|
|
647
|
-
});
|
|
648
|
-
return {
|
|
649
|
-
ok: response.ok,
|
|
650
|
-
status: response.status,
|
|
651
|
-
statusText: response.statusText,
|
|
652
|
-
text: await response.text()
|
|
653
|
-
};
|
|
654
|
-
} finally {
|
|
655
|
-
clearTimeout(timeoutId);
|
|
656
|
-
}
|
|
657
|
-
},
|
|
658
|
-
[url, body, options.timeoutMs || PAGE_CONTEXT_FETCH_TIMEOUT_MS],
|
|
659
|
-
{ timeoutMs: options.timeoutMs, world: "MAIN" }
|
|
660
|
-
).then((result) => parsePageResponse(result, url)), options);
|
|
601
|
+
return withCreatorTab(ctx, async (tab) => {
|
|
602
|
+
options.assertActive?.();
|
|
603
|
+
if (options.deadline && Date.now() >= options.deadline) throw new Error("\u6296\u97F3\u89C6\u9891\u63D0\u4EA4\u5DF2\u8D85\u65F6\uFF0C\u5DF2\u963B\u6B62\u8FDF\u5230\u6295\u7A3F");
|
|
604
|
+
return ctx.Runtime.scripting.execute(
|
|
605
|
+
tab.id,
|
|
606
|
+
async (requestUrl, requestBody, timeoutMs, deadline) => {
|
|
607
|
+
if (deadline && Date.now() >= deadline) throw new Error("\u6296\u97F3\u89C6\u9891\u63D0\u4EA4\u5DF2\u8D85\u65F6\uFF0C\u5DF2\u963B\u6B62\u8FDF\u5230\u6295\u7A3F");
|
|
608
|
+
const controller = new AbortController();
|
|
609
|
+
const remaining = deadline ? Math.min(timeoutMs, deadline - Date.now()) : timeoutMs;
|
|
610
|
+
const timeoutId = setTimeout(() => controller.abort(), remaining);
|
|
611
|
+
try {
|
|
612
|
+
const response = await fetch(requestUrl, {
|
|
613
|
+
method: "POST",
|
|
614
|
+
credentials: "include",
|
|
615
|
+
headers: {
|
|
616
|
+
"Content-Type": "application/json",
|
|
617
|
+
Accept: "application/json, text/plain, */*"
|
|
618
|
+
},
|
|
619
|
+
body: JSON.stringify(requestBody),
|
|
620
|
+
signal: controller.signal
|
|
621
|
+
});
|
|
622
|
+
return {
|
|
623
|
+
ok: response.ok,
|
|
624
|
+
status: response.status,
|
|
625
|
+
statusText: response.statusText,
|
|
626
|
+
passportDecision: response.headers.get("x-tt-verify-passport-decision"),
|
|
627
|
+
text: await response.text()
|
|
628
|
+
};
|
|
629
|
+
} finally {
|
|
630
|
+
clearTimeout(timeoutId);
|
|
631
|
+
}
|
|
632
|
+
},
|
|
633
|
+
[url, body, options.timeoutMs || PAGE_CONTEXT_FETCH_TIMEOUT_MS, options.deadline],
|
|
634
|
+
{ timeoutMs: options.timeoutMs, world: "MAIN" }
|
|
635
|
+
).then((result) => parsePageResponse(result, url));
|
|
636
|
+
}, options);
|
|
661
637
|
}
|
|
662
638
|
async function requestJson(ctx, url, init = {}, options = {}) {
|
|
663
639
|
const response = await ctx.Runtime.network.fetch(url, {
|
|
@@ -670,6 +646,7 @@ async function requestJson(ctx, url, init = {}, options = {}) {
|
|
|
670
646
|
body: init.body,
|
|
671
647
|
timeoutMs: options.timeoutMs
|
|
672
648
|
});
|
|
649
|
+
throwIfPassportVerification(response.headers?.get?.("x-tt-verify-passport-decision"));
|
|
673
650
|
const text = await response.text();
|
|
674
651
|
if (!response.ok) {
|
|
675
652
|
if ([401, 403].includes(response.status) || looksLikeSecurityResponse(text)) {
|
|
@@ -684,6 +661,7 @@ async function requestJson(ctx, url, init = {}, options = {}) {
|
|
|
684
661
|
return parseJsonText(text);
|
|
685
662
|
}
|
|
686
663
|
function parsePageResponse(result, url) {
|
|
664
|
+
throwIfPassportVerification(result?.passportDecision);
|
|
687
665
|
if (!result?.ok) {
|
|
688
666
|
if ([401, 403].includes(result?.status) || looksLikeSecurityResponse(result?.text)) {
|
|
689
667
|
throw createSecurityCheckError(`\u6296\u97F3\u9875\u9762\u4E0A\u4E0B\u6587\u8981\u6C42\u5B89\u5168\u6821\u9A8C: HTTP ${result?.status}`);
|
|
@@ -696,6 +674,18 @@ function parsePageResponse(result, url) {
|
|
|
696
674
|
}
|
|
697
675
|
return parseJsonText(result.text);
|
|
698
676
|
}
|
|
677
|
+
function throwIfPassportVerification(value) {
|
|
678
|
+
if (!value) return;
|
|
679
|
+
let decision;
|
|
680
|
+
try {
|
|
681
|
+
decision = JSON.parse(value);
|
|
682
|
+
} catch {
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
if (decision?.account_flow === "verify") {
|
|
686
|
+
throw createSecurityCheckError("\u6296\u97F3\u8981\u6C42\u8D26\u53F7\u8EAB\u4EFD\u9A8C\u8BC1\uFF0C\u8BF7\u5728\u6296\u97F3\u521B\u4F5C\u8005\u4E2D\u5FC3\u5B8C\u6210\u8EAB\u4EFD\u9A8C\u8BC1\u540E\u91CD\u8BD5");
|
|
687
|
+
}
|
|
688
|
+
}
|
|
699
689
|
function looksLikeSecurityResponse(text) {
|
|
700
690
|
try {
|
|
701
691
|
throwIfSecurityCheck(text);
|
|
@@ -1514,11 +1504,31 @@ async function withDouyinHeaderRules(ctx, input = {}, callback) {
|
|
|
1514
1504
|
return callback();
|
|
1515
1505
|
}
|
|
1516
1506
|
const ruleIds = [];
|
|
1507
|
+
let cleaningUp = false;
|
|
1508
|
+
const removeRule = async (ruleId) => {
|
|
1509
|
+
try {
|
|
1510
|
+
await (ctx.cleanupKit || ctx.Kit).runStep(
|
|
1511
|
+
"\u6296\u97F3\u6E05\u7406\u8BF7\u6C42\u5934\u89C4\u5219",
|
|
1512
|
+
() => headerRules.remove(ruleId, { timeoutMs: input.timeoutMs }),
|
|
1513
|
+
{
|
|
1514
|
+
timeoutMs: input.timeoutMs,
|
|
1515
|
+
data: { platform: DOUYIN_PLATFORM, ruleId }
|
|
1516
|
+
}
|
|
1517
|
+
);
|
|
1518
|
+
} catch (error) {
|
|
1519
|
+
ctx.Logger?.warn?.("\u6296\u97F3\u6E05\u7406\u8BF7\u6C42\u5934\u89C4\u5219\u5931\u8D25", error);
|
|
1520
|
+
}
|
|
1521
|
+
};
|
|
1517
1522
|
try {
|
|
1518
1523
|
for (const rule of DOUYIN_HEADER_RULES) {
|
|
1519
|
-
|
|
1524
|
+
await ctx.Kit.runStep(
|
|
1520
1525
|
"\u6296\u97F3\u6DFB\u52A0\u8BF7\u6C42\u5934\u89C4\u5219",
|
|
1521
|
-
() =>
|
|
1526
|
+
async () => {
|
|
1527
|
+
const ruleId = await headerRules.add(rule, { timeoutMs: 0 });
|
|
1528
|
+
if (cleaningUp) await removeRule(ruleId);
|
|
1529
|
+
else ruleIds.push(ruleId);
|
|
1530
|
+
return ruleId;
|
|
1531
|
+
},
|
|
1522
1532
|
{
|
|
1523
1533
|
timeoutMs: input.timeoutMs,
|
|
1524
1534
|
data: {
|
|
@@ -1527,26 +1537,12 @@ async function withDouyinHeaderRules(ctx, input = {}, callback) {
|
|
|
1527
1537
|
}
|
|
1528
1538
|
}
|
|
1529
1539
|
);
|
|
1530
|
-
ruleIds.push(ruleId);
|
|
1531
1540
|
}
|
|
1532
1541
|
return await callback();
|
|
1533
1542
|
} finally {
|
|
1543
|
+
cleaningUp = true;
|
|
1534
1544
|
for (const ruleId of ruleIds.reverse()) {
|
|
1535
|
-
|
|
1536
|
-
await ctx.Kit.runStep(
|
|
1537
|
-
"\u6296\u97F3\u6E05\u7406\u8BF7\u6C42\u5934\u89C4\u5219",
|
|
1538
|
-
() => headerRules.remove(ruleId, { timeoutMs: input.timeoutMs }),
|
|
1539
|
-
{
|
|
1540
|
-
timeoutMs: input.timeoutMs,
|
|
1541
|
-
data: {
|
|
1542
|
-
platform: DOUYIN_PLATFORM,
|
|
1543
|
-
ruleId
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
|
-
);
|
|
1547
|
-
} catch (error) {
|
|
1548
|
-
ctx.Logger?.warn?.("\u6296\u97F3\u6E05\u7406\u8BF7\u6C42\u5934\u89C4\u5219\u5931\u8D25", error);
|
|
1549
|
-
}
|
|
1545
|
+
await removeRule(ruleId);
|
|
1550
1546
|
}
|
|
1551
1547
|
}
|
|
1552
1548
|
}
|
|
@@ -1807,7 +1803,7 @@ async function publishDouyinVideo(ctx, media, input = {}, dependencies = createV
|
|
|
1807
1803
|
);
|
|
1808
1804
|
const payload = await ctx.Kit.runStep(
|
|
1809
1805
|
"\u6296\u97F3\u6784\u9020\u89C6\u9891\u53D1\u5E03\u53C2\u6570",
|
|
1810
|
-
() =>
|
|
1806
|
+
() => ({ item: buildVideoPublishItem(media, video, posterUri, cover, resolvedTags, input.scheduleTime) }),
|
|
1811
1807
|
{
|
|
1812
1808
|
timeoutMs: input.timeoutMs,
|
|
1813
1809
|
data: {
|
|
@@ -1819,22 +1815,34 @@ async function publishDouyinVideo(ctx, media, input = {}, dependencies = createV
|
|
|
1819
1815
|
}
|
|
1820
1816
|
}
|
|
1821
1817
|
);
|
|
1818
|
+
let submitStarted = false;
|
|
1822
1819
|
const response = await ctx.Kit.runStep(
|
|
1823
1820
|
"\u6296\u97F3\u63D0\u4EA4\u89C6\u9891\u53D1\u5E03",
|
|
1824
|
-
() =>
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1821
|
+
() => {
|
|
1822
|
+
ctx.publishLifecycle?.assertActive();
|
|
1823
|
+
if (submitStarted) throw new Error("\u6296\u97F3\u89C6\u9891\u5DF2\u63D0\u4EA4\uFF0C\u5DF2\u963B\u6B62\u91CD\u590D\u6295\u7A3F");
|
|
1824
|
+
submitStarted = true;
|
|
1825
|
+
return dependencies.postCreatorJson(
|
|
1826
|
+
`${CREATOR_ORIGIN}/web/api/media/aweme/create_v2/?read_aid=${DOUYIN_READ_AID}`,
|
|
1827
|
+
payload,
|
|
1828
|
+
{
|
|
1829
|
+
preferPageContext: true,
|
|
1830
|
+
pageContextOnly: true,
|
|
1831
|
+
assertActive: ctx.publishLifecycle?.assertActive,
|
|
1832
|
+
deadline: ctx.publishLifecycle?.getDeadline(),
|
|
1833
|
+
timeoutMs: input.timeoutMs
|
|
1834
|
+
}
|
|
1835
|
+
);
|
|
1836
|
+
},
|
|
1832
1837
|
{
|
|
1833
1838
|
timeoutMs: input.timeoutMs,
|
|
1839
|
+
retry: { times: 0 },
|
|
1834
1840
|
data: {
|
|
1835
1841
|
platform: DOUYIN_PLATFORM,
|
|
1842
|
+
requestPath: "/web/api/media/aweme/create_v2/",
|
|
1843
|
+
contentType: "application/json",
|
|
1836
1844
|
videoId: video.vid,
|
|
1837
|
-
textLength: payload.text
|
|
1845
|
+
textLength: payload.item.common.text.length
|
|
1838
1846
|
}
|
|
1839
1847
|
}
|
|
1840
1848
|
);
|
|
@@ -1854,7 +1862,7 @@ async function publishDouyinVideo(ctx, media, input = {}, dependencies = createV
|
|
|
1854
1862
|
meta: {
|
|
1855
1863
|
kind: "video",
|
|
1856
1864
|
videoId: video.vid,
|
|
1857
|
-
...
|
|
1865
|
+
...payload.item.common.timing ? { timing: payload.item.common.timing } : {}
|
|
1858
1866
|
}
|
|
1859
1867
|
}
|
|
1860
1868
|
};
|
|
@@ -1874,7 +1882,7 @@ function createVideoDependencies(ctx, input) {
|
|
|
1874
1882
|
(url) => getCreatorJson(ctx, url, {}, { timeoutMs: input?.timeoutMs }),
|
|
1875
1883
|
{ timeoutMs: input?.timeoutMs }
|
|
1876
1884
|
),
|
|
1877
|
-
|
|
1885
|
+
postCreatorJson: (url, body, options) => postCreatorJson(ctx, url, body, options)
|
|
1878
1886
|
};
|
|
1879
1887
|
}
|
|
1880
1888
|
|
|
@@ -1903,7 +1911,7 @@ var platform = Object.freeze({
|
|
|
1903
1911
|
video: {
|
|
1904
1912
|
isOk: true,
|
|
1905
1913
|
titleMin: 0,
|
|
1906
|
-
titleMax:
|
|
1914
|
+
titleMax: 30,
|
|
1907
1915
|
tagsMin: 0,
|
|
1908
1916
|
tagsMax: 5,
|
|
1909
1917
|
contentMax: 1,
|
|
@@ -12833,6 +12841,7 @@ async function publishOperation(ctx, adapterLookup, input = {}) {
|
|
|
12833
12841
|
platform: platform15
|
|
12834
12842
|
});
|
|
12835
12843
|
}
|
|
12844
|
+
let publishContext = ctx;
|
|
12836
12845
|
try {
|
|
12837
12846
|
const authInput = withDefaultTimeout(input, OperationTimeoutMs.CheckAuth);
|
|
12838
12847
|
const publishInput = withDefaultTimeout(input, OperationTimeoutMs.Publish);
|
|
@@ -12846,13 +12855,22 @@ async function publishOperation(ctx, adapterLookup, input = {}) {
|
|
|
12846
12855
|
platform: platform15
|
|
12847
12856
|
});
|
|
12848
12857
|
}
|
|
12849
|
-
const
|
|
12858
|
+
const guardedVideo = platform15 === "douyin" && media.type === "video";
|
|
12859
|
+
if (guardedVideo) publishContext = createDouyinVideoPublishContext(ctx, publishInput.timeoutMs);
|
|
12860
|
+
let publishStarted = false;
|
|
12861
|
+
const publishOptions = createStepOptions(publishInput, {
|
|
12862
|
+
platform: platform15,
|
|
12863
|
+
type: media.type
|
|
12864
|
+
}, OperationTimeoutMs.Publish);
|
|
12865
|
+
if (guardedVideo) publishOptions.retry = { times: 0 };
|
|
12866
|
+
const publishResult = await publishContext.Kit.runStep(
|
|
12850
12867
|
`${platform15}: \u53D1\u5E03\u5185\u5BB9`,
|
|
12851
|
-
() =>
|
|
12852
|
-
|
|
12853
|
-
|
|
12854
|
-
|
|
12855
|
-
},
|
|
12868
|
+
() => {
|
|
12869
|
+
if (guardedVideo && publishStarted) throw new Error("\u6296\u97F3\u89C6\u9891\u5DF2\u6267\u884C\uFF0C\u5DF2\u963B\u6B62\u91CD\u590D\u53D1\u5E03");
|
|
12870
|
+
publishStarted = true;
|
|
12871
|
+
return adapter.publish(publishContext, media, publishInput);
|
|
12872
|
+
},
|
|
12873
|
+
publishOptions
|
|
12856
12874
|
);
|
|
12857
12875
|
if (publishResult?.securityCheckRequired === true) {
|
|
12858
12876
|
return createFailure(StatusCode.SecurityCheckRequired, publishResult.reason ?? "\u5E73\u53F0\u8981\u6C42\u5B89\u5168\u6821\u9A8C", {
|
|
@@ -12875,7 +12893,57 @@ async function publishOperation(ctx, adapterLookup, input = {}) {
|
|
|
12875
12893
|
return mapErrorToResult(error, StatusCode.Unknown, {
|
|
12876
12894
|
platform: platform15
|
|
12877
12895
|
});
|
|
12896
|
+
} finally {
|
|
12897
|
+
if (publishContext !== ctx) publishContext.publishLifecycle.close();
|
|
12898
|
+
}
|
|
12899
|
+
}
|
|
12900
|
+
function createDouyinVideoPublishContext(ctx, timeoutMs) {
|
|
12901
|
+
let active = true;
|
|
12902
|
+
const deadline = Date.now() + timeoutMs;
|
|
12903
|
+
const stepDeadlines = /* @__PURE__ */ new Set();
|
|
12904
|
+
const getDeadline = () => Math.min(deadline, ...Array.from(stepDeadlines, (step) => step.deadline));
|
|
12905
|
+
const assertActive = () => {
|
|
12906
|
+
if (!active || Date.now() >= getDeadline()) {
|
|
12907
|
+
active = false;
|
|
12908
|
+
throw Object.assign(new Error("\u6296\u97F3\u89C6\u9891\u53D1\u5E03\u5DF2\u7ED3\u675F\u6216\u8D85\u65F6\uFF0C\u5DF2\u963B\u6B62\u8FDF\u5230\u6B65\u9AA4"), { code: 30000003 });
|
|
12909
|
+
}
|
|
12910
|
+
};
|
|
12911
|
+
const Kit = { ...ctx.Kit };
|
|
12912
|
+
for (const method of ["runStep", "runStepLoose"]) {
|
|
12913
|
+
if (typeof ctx.Kit[method] !== "function") continue;
|
|
12914
|
+
Kit[method] = async (name, action, options = {}) => {
|
|
12915
|
+
assertActive();
|
|
12916
|
+
const step = { deadline: Date.now() + (options.timeoutMs ?? 6e4) };
|
|
12917
|
+
try {
|
|
12918
|
+
return await ctx.Kit[method](name, async (...args) => {
|
|
12919
|
+
stepDeadlines.add(step);
|
|
12920
|
+
try {
|
|
12921
|
+
assertActive();
|
|
12922
|
+
const result = await action(...args);
|
|
12923
|
+
assertActive();
|
|
12924
|
+
return result;
|
|
12925
|
+
} finally {
|
|
12926
|
+
stepDeadlines.delete(step);
|
|
12927
|
+
}
|
|
12928
|
+
}, options);
|
|
12929
|
+
} catch (error) {
|
|
12930
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12931
|
+
for (let cause = error; cause && !seen.has(cause); cause = cause.cause) {
|
|
12932
|
+
seen.add(cause);
|
|
12933
|
+
if ([30000003, 30000008].includes(cause.code) || cause.name === "TimeoutError") active = false;
|
|
12934
|
+
}
|
|
12935
|
+
throw error;
|
|
12936
|
+
}
|
|
12937
|
+
};
|
|
12878
12938
|
}
|
|
12939
|
+
return {
|
|
12940
|
+
...ctx,
|
|
12941
|
+
Kit,
|
|
12942
|
+
cleanupKit: ctx.Kit,
|
|
12943
|
+
publishLifecycle: { assertActive, getDeadline, close: () => {
|
|
12944
|
+
active = false;
|
|
12945
|
+
} }
|
|
12946
|
+
};
|
|
12879
12947
|
}
|
|
12880
12948
|
|
|
12881
12949
|
// src/operations/get-status.js
|