specific-ai-cli 0.0.0-stage → 0.1.1

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.
Files changed (277) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +177 -0
  3. package/README.ja.md +177 -0
  4. package/README.ko.md +177 -0
  5. package/README.md +174 -2
  6. package/README.zh-TW.md +177 -0
  7. package/assets/banner.txt +3 -0
  8. package/dist/cli/argv.js +98 -0
  9. package/dist/cli/commands/auth.js +11 -0
  10. package/dist/cli/commands/config.js +23 -0
  11. package/dist/cli/commands/doctor.js +134 -0
  12. package/dist/cli/commands/platforms.js +24 -0
  13. package/dist/cli/commands/version.js +4 -0
  14. package/dist/cli/confirm.js +22 -0
  15. package/dist/cli/dispatch.js +440 -0
  16. package/dist/cli/help.js +223 -0
  17. package/dist/cli/hints.js +60 -0
  18. package/dist/cli/main.js +18 -0
  19. package/dist/cli/output.js +66 -0
  20. package/dist/core/auth-handlers.js +41 -0
  21. package/dist/core/auth-store.js +119 -0
  22. package/dist/core/capabilities.js +163 -0
  23. package/dist/core/config.js +121 -0
  24. package/dist/core/cookies.js +164 -0
  25. package/dist/core/errors.js +37 -0
  26. package/dist/core/files.js +89 -0
  27. package/dist/core/fsutil.js +52 -0
  28. package/dist/core/hash.js +4 -0
  29. package/dist/core/http.js +331 -0
  30. package/dist/core/image.js +280 -0
  31. package/dist/core/log.js +40 -0
  32. package/dist/core/login.js +191 -0
  33. package/dist/core/mp4.js +130 -0
  34. package/dist/core/normalize.js +207 -0
  35. package/dist/core/options.js +110 -0
  36. package/dist/core/paths.js +38 -0
  37. package/dist/core/pb.js +59 -0
  38. package/dist/core/py.js +181 -0
  39. package/dist/core/rand.js +131 -0
  40. package/dist/core/registry.js +129 -0
  41. package/dist/core/schemas.js +41 -0
  42. package/dist/core/stream.js +104 -0
  43. package/dist/core/toolkit.js +35 -0
  44. package/dist/core/vm.js +73 -0
  45. package/dist/core/vocab.js +157 -0
  46. package/dist/platforms/_shared/impaas.js +410 -0
  47. package/dist/platforms/bilibili/index.js +216 -0
  48. package/dist/platforms/bilibili/web/api.js +806 -0
  49. package/dist/platforms/bilibili/web/client.js +311 -0
  50. package/dist/platforms/bilibili/web/commands.js +783 -0
  51. package/dist/platforms/bilibili/web/ddddocr.js +342 -0
  52. package/dist/platforms/bilibili/web/gaia.js +130 -0
  53. package/dist/platforms/bilibili/web/geetest-manual.js +154 -0
  54. package/dist/platforms/bilibili/web/geetest-vision.js +232 -0
  55. package/dist/platforms/bilibili/web/geetest.js +450 -0
  56. package/dist/platforms/bilibili/web/normalize.js +234 -0
  57. package/dist/platforms/bilibili/web/profile.js +135 -0
  58. package/dist/platforms/bilibili/web/resolve.js +112 -0
  59. package/dist/platforms/bilibili/web/sign.js +223 -0
  60. package/dist/platforms/bilibili/web/upos.js +102 -0
  61. package/dist/platforms/douyin/index.js +142 -0
  62. package/dist/platforms/douyin/web/api.js +863 -0
  63. package/dist/platforms/douyin/web/client.js +413 -0
  64. package/dist/platforms/douyin/web/commands.js +712 -0
  65. package/dist/platforms/douyin/web/creator.js +601 -0
  66. package/dist/platforms/douyin/web/crypto.js +247 -0
  67. package/dist/platforms/douyin/web/dtrait.js +190 -0
  68. package/dist/platforms/douyin/web/im.js +502 -0
  69. package/dist/platforms/douyin/web/image.js +5 -0
  70. package/dist/platforms/douyin/web/live.js +225 -0
  71. package/dist/platforms/douyin/web/mssdk.js +148 -0
  72. package/dist/platforms/douyin/web/normalize.js +326 -0
  73. package/dist/platforms/douyin/web/passport.js +1247 -0
  74. package/dist/platforms/douyin/web/profile.js +221 -0
  75. package/dist/platforms/douyin/web/proto.js +26 -0
  76. package/dist/platforms/douyin/web/resolve.js +88 -0
  77. package/dist/platforms/douyin/web/sign.js +436 -0
  78. package/dist/platforms/douyin/web/tos.js +78 -0
  79. package/dist/platforms/index.js +17 -0
  80. package/dist/platforms/jd/index.js +67 -0
  81. package/dist/platforms/jd/web/aks.js +115 -0
  82. package/dist/platforms/jd/web/api.js +549 -0
  83. package/dist/platforms/jd/web/chat.js +132 -0
  84. package/dist/platforms/jd/web/client.js +470 -0
  85. package/dist/platforms/jd/web/commands.js +557 -0
  86. package/dist/platforms/jd/web/jcap/helper.js +90 -0
  87. package/dist/platforms/jd/web/jcap/host.js +107 -0
  88. package/dist/platforms/jd/web/jcap/lsd.js +300 -0
  89. package/dist/platforms/jd/web/jcap/solver.js +995 -0
  90. package/dist/platforms/jd/web/jcap.js +150 -0
  91. package/dist/platforms/jd/web/js.js +420 -0
  92. package/dist/platforms/jd/web/login.js +775 -0
  93. package/dist/platforms/jd/web/normalize.js +297 -0
  94. package/dist/platforms/jd/web/profile.js +256 -0
  95. package/dist/platforms/jd/web/util.js +175 -0
  96. package/dist/platforms/jd/web/webm.js +133 -0
  97. package/dist/platforms/kuaishou/index.js +98 -0
  98. package/dist/platforms/kuaishou/web/api.js +694 -0
  99. package/dist/platforms/kuaishou/web/captcha-crypto.js +134 -0
  100. package/dist/platforms/kuaishou/web/captcha-fp.js +180 -0
  101. package/dist/platforms/kuaishou/web/captcha.js +273 -0
  102. package/dist/platforms/kuaishou/web/client.js +487 -0
  103. package/dist/platforms/kuaishou/web/commands.js +863 -0
  104. package/dist/platforms/kuaishou/web/gap.js +144 -0
  105. package/dist/platforms/kuaishou/web/gdfp.js +376 -0
  106. package/dist/platforms/kuaishou/web/gql.js +11 -0
  107. package/dist/platforms/kuaishou/web/live.js +125 -0
  108. package/dist/platforms/kuaishou/web/normalize.js +241 -0
  109. package/dist/platforms/kuaishou/web/oracle.js +329 -0
  110. package/dist/platforms/kuaishou/web/profile.js +259 -0
  111. package/dist/platforms/kuaishou/web/resolve.js +84 -0
  112. package/dist/platforms/kuaishou/web/session.js +592 -0
  113. package/dist/platforms/kuaishou/web/sign.js +568 -0
  114. package/dist/platforms/taobao/index.js +56 -0
  115. package/dist/platforms/taobao/web/api.js +96 -0
  116. package/dist/platforms/taobao/web/client.js +102 -0
  117. package/dist/platforms/taobao/web/commands.js +172 -0
  118. package/dist/platforms/taobao/web/im.js +68 -0
  119. package/dist/platforms/taobao/web/normalize.js +103 -0
  120. package/dist/platforms/taobao/web/profile.js +85 -0
  121. package/dist/platforms/taobao/web/resolve.js +51 -0
  122. package/dist/platforms/taobao/web/sign.js +35 -0
  123. package/dist/platforms/tiktok/index.js +120 -0
  124. package/dist/platforms/tiktok/web/api.js +1234 -0
  125. package/dist/platforms/tiktok/web/client.js +362 -0
  126. package/dist/platforms/tiktok/web/commands.js +821 -0
  127. package/dist/platforms/tiktok/web/im.js +133 -0
  128. package/dist/platforms/tiktok/web/jsrun.js +126 -0
  129. package/dist/platforms/tiktok/web/normalize.js +286 -0
  130. package/dist/platforms/tiktok/web/profile.js +169 -0
  131. package/dist/platforms/tiktok/web/resolve.js +156 -0
  132. package/dist/platforms/tiktok/web/sign.js +585 -0
  133. package/dist/platforms/tiktok/web/upload.js +805 -0
  134. package/dist/platforms/tiktok/web/wire.js +490 -0
  135. package/dist/platforms/weibo/index.js +91 -0
  136. package/dist/platforms/weibo/web/api.js +206 -0
  137. package/dist/platforms/weibo/web/client.js +155 -0
  138. package/dist/platforms/weibo/web/commands.js +202 -0
  139. package/dist/platforms/weibo/web/normalize.js +137 -0
  140. package/dist/platforms/weibo/web/profile.js +246 -0
  141. package/dist/platforms/weibo/web/resolve.js +60 -0
  142. package/dist/platforms/weibo/web/sign.js +85 -0
  143. package/dist/platforms/x/index.js +107 -0
  144. package/dist/platforms/x/web/api.js +443 -0
  145. package/dist/platforms/x/web/article.js +212 -0
  146. package/dist/platforms/x/web/client.js +202 -0
  147. package/dist/platforms/x/web/commands.js +363 -0
  148. package/dist/platforms/x/web/graphql.js +108 -0
  149. package/dist/platforms/x/web/normalize.js +235 -0
  150. package/dist/platforms/x/web/profile.js +99 -0
  151. package/dist/platforms/x/web/resolve.js +44 -0
  152. package/dist/platforms/x/web/transaction.js +171 -0
  153. package/dist/platforms/xhs/index.js +101 -0
  154. package/dist/platforms/xhs/web/api.js +278 -0
  155. package/dist/platforms/xhs/web/client.js +540 -0
  156. package/dist/platforms/xhs/web/commands.js +1046 -0
  157. package/dist/platforms/xhs/web/creator-api.js +609 -0
  158. package/dist/platforms/xhs/web/creator.js +203 -0
  159. package/dist/platforms/xhs/web/js.js +194 -0
  160. package/dist/platforms/xhs/web/login.js +230 -0
  161. package/dist/platforms/xhs/web/normalize.js +166 -0
  162. package/dist/platforms/xhs/web/profile.js +127 -0
  163. package/dist/platforms/xhs/web/push.js +361 -0
  164. package/dist/platforms/xhs/web/resolve.js +69 -0
  165. package/dist/platforms/xhs/web/state.js +368 -0
  166. package/dist/platforms/xianyu/index.js +59 -0
  167. package/dist/platforms/xianyu/web/api.js +312 -0
  168. package/dist/platforms/xianyu/web/client.js +134 -0
  169. package/dist/platforms/xianyu/web/commands.js +305 -0
  170. package/dist/platforms/xianyu/web/im.js +73 -0
  171. package/dist/platforms/xianyu/web/normalize.js +123 -0
  172. package/dist/platforms/xianyu/web/profile.js +146 -0
  173. package/dist/platforms/xianyu/web/resolve.js +48 -0
  174. package/dist/platforms/xianyu/web/sign.js +79 -0
  175. package/package.json +77 -4
  176. package/static/douyin/Live.proto +194 -0
  177. package/static/douyin/Request.proto +89 -0
  178. package/static/douyin/Response.proto +46 -0
  179. package/static/douyin/acrawler_runtime/ac_vm.js +2 -0
  180. package/static/douyin/acrawler_runtime/browser_window_shape.json +1 -0
  181. package/static/douyin/acrawler_runtime/canvas_actual_exact.json +1 -0
  182. package/static/douyin/acrawler_runtime/run_ac_node.js +1005 -0
  183. package/static/douyin/acrawler_runtime/runner_window_keys_baseline.txt +240 -0
  184. package/static/douyin/challenge_profile.json +801 -0
  185. package/static/douyin/challenge_template_runner.js +217 -0
  186. package/static/douyin/dtrait_profile.json +66 -0
  187. package/static/douyin/mstoken_common_profile.json +1 -0
  188. package/static/douyin/mstoken_profile.json +214 -0
  189. package/static/jd/h5st5_env.js +750 -0
  190. package/static/jd/h5st5_lib.js +5 -0
  191. package/static/jd/h5st5_server.js +49 -0
  192. package/static/jd/jcap/env/env_core.js +512 -0
  193. package/static/jd/jcap/env/run.js +1307 -0
  194. package/static/jd/jcap/run/jcap_ujb96b.js +39 -0
  195. package/static/jd/package.json +4 -0
  196. package/static/jd/pc_tk_lib.js +1 -0
  197. package/static/jd/pc_tk_server.js +63 -0
  198. package/static/jd/summer_cryptico_runner.js +50 -0
  199. package/static/jd/webm/env/run.js +453 -0
  200. package/static/jd/webm/run/jdwebm-riskhandle.js +49 -0
  201. package/static/kuaishou/bundles/weapon/kwf-0.0.2.2cee19b4b7dec496.js +1 -0
  202. package/static/kuaishou/bundles/weapon/kws-0-0.0.1-obfuscated.4b7f3de1ab0ddcc2.js +1 -0
  203. package/static/kuaishou/bundles/weapon/kws-1-0.0.1-obfuscated.346ebdb1c78df316.js +1 -0
  204. package/static/kuaishou/bundles/weapon/kws-10-0.0.1-obfuscated.a33db41897b983af.js +1 -0
  205. package/static/kuaishou/bundles/weapon/kws-11-0.0.1-obfuscated.5e0a90af726d8a7e.js +1 -0
  206. package/static/kuaishou/bundles/weapon/kws-12-0.0.1-obfuscated.20ea29345706ec8c.js +1 -0
  207. package/static/kuaishou/bundles/weapon/kws-13-0.0.1-obfuscated.6b74e9640ff18648.js +1 -0
  208. package/static/kuaishou/bundles/weapon/kws-14-0.0.1-obfuscated.551e4429d156bf66.js +1 -0
  209. package/static/kuaishou/bundles/weapon/kws-15-0.0.1-obfuscated.8625b087aaba4a60.js +1 -0
  210. package/static/kuaishou/bundles/weapon/kws-16-0.0.1-obfuscated.d5ffa4020f3be99a.js +1 -0
  211. package/static/kuaishou/bundles/weapon/kws-17-0.0.1-obfuscated.6d13e19b22f50a80.js +1 -0
  212. package/static/kuaishou/bundles/weapon/kws-18-0.0.1-obfuscated.6d31405044166289.js +1 -0
  213. package/static/kuaishou/bundles/weapon/kws-19-0.0.1-obfuscated.a9a8d925942f19e4.js +1 -0
  214. package/static/kuaishou/bundles/weapon/kws-2-0.0.1-obfuscated.b849a136ae4a6b3e.js +1 -0
  215. package/static/kuaishou/bundles/weapon/kws-3-0.0.1-obfuscated.f9e77ebf6186e2ac.js +1 -0
  216. package/static/kuaishou/bundles/weapon/kws-4-0.0.1-obfuscated.2c3c155aa52ceb0e.js +1 -0
  217. package/static/kuaishou/bundles/weapon/kws-5-0.0.1-obfuscated.4e553d17691b3c5a.js +1 -0
  218. package/static/kuaishou/bundles/weapon/kws-6-0.0.1-obfuscated.f73ddd3ff13e1217.js +1 -0
  219. package/static/kuaishou/bundles/weapon/kws-7-0.0.1-obfuscated.1628b8729beaab7e.js +1 -0
  220. package/static/kuaishou/bundles/weapon/kws-8-0.0.1-obfuscated.6da6baef7aa81fff.js +1 -0
  221. package/static/kuaishou/bundles/weapon/kws-9-0.0.1-obfuscated.8645d55e5323b743.js +1 -0
  222. package/static/kuaishou/fixtures/current_follow_script_813.network-response +2 -0
  223. package/static/kuaishou/fixtures/live_ws_maps.json +61 -0
  224. package/static/kuaishou/fixtures/live_ws_proto.json +1342 -0
  225. package/static/kuaishou/js/cp-kwf.js +1 -0
  226. package/static/kuaishou/tools/kwf_oracle.js +799 -0
  227. package/static/kuaishou/tools/kws_oracle.js +885 -0
  228. package/static/kuaishou/tools/like_token_oracle.js +70 -0
  229. package/static/taobao/taobao_js_20260407.js +570 -0
  230. package/static/tiktok/Tiktok_Request.json +597 -0
  231. package/static/tiktok/package.json +3 -0
  232. package/static/tiktok/shop_bsid/core.js +1 -0
  233. package/static/tiktok/shop_bsid/env_core.js +512 -0
  234. package/static/tiktok/shop_bsid/loader.js +1 -0
  235. package/static/tiktok/shop_bsid/run.js +753 -0
  236. package/static/tiktok/shop_bsid/sign.js +100 -0
  237. package/static/tiktok/shop_bsid/webmssdk.js +1 -0
  238. package/static/tiktok/shop_bsid/webmssdk_ex.js +1 -0
  239. package/static/tiktok/signing/env/env_core.js +512 -0
  240. package/static/tiktok/signing/env/sign.js +455 -0
  241. package/static/tiktok/signing/sdk_config.json +230 -0
  242. package/static/tiktok/signing/source/webmssdk.js +1 -0
  243. package/static/tiktok/signing/source/webmssdk_ex.js +1 -0
  244. package/static/tiktok/signing/source/webmssdk_legacy.js +1 -0
  245. package/static/x/graphql.json +7777 -0
  246. package/static/x/transaction_l1.json +12 -0
  247. package/static/xhs/package.json +3 -0
  248. package/static/xhs/xhs_core/js/b1.js +210 -0
  249. package/static/xhs/xhs_core/js/mns.js +340 -0
  250. package/static/xhs/xhs_core/js/mns_0101_keystream.json +5 -0
  251. package/static/xhs/xhs_core/js/mns_keystreams.json +1 -0
  252. package/static/xhs/xhs_core/js/sign.js +225 -0
  253. package/static/xhs/xhs_core/js/websectiga_cli.js +220 -0
  254. package/static/xhs/xhs_core/js/websectiga_env.js +512 -0
  255. package/static/xhs/xhs_core/js/xs_common.js +79 -0
  256. package/static/xhs/xhs_creator/js/profile.js +115 -0
  257. package/static/xhs/xhs_creator/js/rap_fingerprint_creator.json +6 -0
  258. package/static/xhs/xhs_creator/js/reference_profile.json +282 -0
  259. package/static/xhs/xhs_creator/js/xhs_creator_sign.js +29 -0
  260. package/static/xhs/xhs_creator/js/xhs_creator_signature.js +23 -0
  261. package/static/xhs/xhs_pc/js/aes_decrypt.js +22 -0
  262. package/static/xhs/xhs_pc/js/aes_encrypt.js +30 -0
  263. package/static/xhs/xhs_pc/js/b1.js +9 -0
  264. package/static/xhs/xhs_pc/js/deflate.js +320 -0
  265. package/static/xhs/xhs_pc/js/mns.js +4 -0
  266. package/static/xhs/xhs_pc/js/profile.js +141 -0
  267. package/static/xhs/xhs_pc/js/rap.js +173 -0
  268. package/static/xhs/xhs_pc/js/rap_cli.js +30 -0
  269. package/static/xhs/xhs_pc/js/rap_crypto.js +73 -0
  270. package/static/xhs/xhs_pc/js/rap_fingerprint_template.json +8 -0
  271. package/static/xhs/xhs_pc/js/reference_profile.json +262 -0
  272. package/static/xhs/xhs_pc/js/sign.js +6 -0
  273. package/static/xhs/xhs_pc/js/web_ssk.js +82 -0
  274. package/static/xhs/xhs_pc/js/xs_common.js +4 -0
  275. package/static/xianyu/et_f.js +2 -0
  276. package/static/xianyu/gen_tfstk.js +410 -0
  277. package/static/xianyu/goofish_js_version_2.js +568 -0
@@ -0,0 +1,601 @@
1
+ import { SpecificAIError } from '../../../core/errors.js';
2
+ import { compactJson, jsonLoads } from '../../../core/py.js';
3
+ import * as rand from '../../../core/rand.js';
4
+ import { authError } from '../../../core/toolkit.js';
5
+ import * as api from './api.js';
6
+ import { riskJson } from './client.js';
7
+ import { crc32Hex, IMAGEX_HOST, sigv4, VOD_HOST } from './crypto.js';
8
+ import { DTRAIT_BROKEN, DTRAIT_HINT } from './dtrait.js';
9
+ import { imageSize } from './image.js';
10
+ import { CREATOR, creatorPlatformParams, Headers, Params, PROFILE, WWW_ONLY } from './profile.js';
11
+ import { spliceUrl, svWebId } from './sign.js';
12
+ import { CREATOR_TOS, tosUpload, VOD_VERSION } from './tos.js';
13
+ /**
14
+ * 创作者中心(上游 dy_apis/douyin_creator_api.py 与 DouyinAuth.bootstrap_creator_session / creator_cookie_str):
15
+ * 图文 / 视频发布走 ImageX / VOD 上传,再 POST create_v2。
16
+ */
17
+ const HOST = 'creator.douyin.com';
18
+ const IMAGEX_SERVICE_ID = 'jm8ajry58r';
19
+ const IMAGEX_APP_ID = '2906';
20
+ const READ_AID = '2906';
21
+ const CSRF_PROBE = '/web/api/media/anchor/search';
22
+ export const POST_IMAGE_REFERER = `${CREATOR}/creator-micro/content/post/image?enter_from=publish_page&media_type=image&type=new`;
23
+ export const POST_VIDEO_REFERER = `${CREATOR}/creator-micro/content/post/video?enter_from=publish_page`;
24
+ // ---------------------------------------------------------------- 会话
25
+ const bootstrapped = new WeakSet();
26
+ const csrfTokens = new WeakMap();
27
+ const randomPools = new WeakMap();
28
+ /** 进入创作者中心页面时的 cookie 初始化与 csrf token(上游 bootstrap_creator_session)。 */
29
+ export async function bootstrap(d) {
30
+ if (bootstrapped.has(d))
31
+ return;
32
+ d.creatorJar.splice(0);
33
+ const referer = `${CREATOR}/creator-micro/content/upload`;
34
+ const page = await d.request({
35
+ url: referer,
36
+ headers: [
37
+ ['sec-ch-ua-platform', PROFILE.secChUaPlatform],
38
+ ['upgrade-insecure-requests', '1'],
39
+ ['user-agent', PROFILE.ua],
40
+ ['accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7'],
41
+ ['sec-ch-ua', PROFILE.secChUa],
42
+ ['sec-ch-ua-mobile', '?0'],
43
+ ['sec-fetch-site', 'none'],
44
+ ['sec-fetch-mode', 'navigate'],
45
+ ['sec-fetch-user', '?1'],
46
+ ['sec-fetch-dest', 'document'],
47
+ ['accept-language', PROFILE.acceptLanguage],
48
+ ['priority', 'u=0, i'],
49
+ ],
50
+ });
51
+ if (page.status >= 400)
52
+ throw new SpecificAIError('UPSTREAM', `打开创作者中心失败(HTTP ${page.status})`, { detail: { status: page.status } });
53
+ try {
54
+ await d.request({
55
+ url: `${CREATOR}/aweme/v1/web/oversea/judgment/`,
56
+ headers: [
57
+ ['origin', CREATOR],
58
+ ['accept', '*/*'],
59
+ ['sec-fetch-site', 'same-origin'],
60
+ ['sec-fetch-mode', 'cors'],
61
+ ['sec-fetch-dest', 'empty'],
62
+ ['referer', referer],
63
+ ['user-agent', PROFILE.ua],
64
+ ['accept-language', PROFILE.acceptLanguage],
65
+ ],
66
+ timeout: 20,
67
+ });
68
+ }
69
+ catch { }
70
+ const jar = d.creatorJar;
71
+ const put = (name, value, domain) => {
72
+ const i = jar.findIndex((c) => c.name === name && c.domain === domain);
73
+ if (i >= 0)
74
+ jar.splice(i, 1);
75
+ jar.push({ name, value, domain, path: '/', expires: null });
76
+ };
77
+ for (let i = jar.length - 1; i >= 0; i--)
78
+ if (jar[i].name === 'x-web-secsdk-uid' && jar[i].domain === '.creator.douyin.com')
79
+ jar.splice(i, 1);
80
+ put('x-web-secsdk-uid', rand.uuid4(), HOST);
81
+ if (!jar.some((c) => c.name === 'gfkadpd') && !d.cookie('gfkadpd'))
82
+ put('gfkadpd', '2906,33638', '.creator.douyin.com');
83
+ if (!jar.some((c) => c.name === '_tea_utm_cache_2906') && !d.cookie('_tea_utm_cache_2906'))
84
+ put('_tea_utm_cache_2906', 'undefined', '.creator.douyin.com');
85
+ const csrf = await d.request({
86
+ method: 'HEAD',
87
+ url: CREATOR + CSRF_PROBE,
88
+ headers: [
89
+ ['x-secsdk-csrf-request', '1'],
90
+ ['referer', referer],
91
+ ['user-agent', PROFILE.ua],
92
+ ['x-secsdk-csrf-version', '1.2.22'],
93
+ ['accept', '*/*'],
94
+ ['accept-language', PROFILE.acceptLanguage],
95
+ ],
96
+ });
97
+ const parts = (csrf.headers.get('x-ware-csrf-token') ?? '').split(',');
98
+ csrfTokens.set(d, parts.length > 1 ? parts[1] : '');
99
+ put('s_v_web_id', svWebId(), HOST);
100
+ bootstrapped.add(d);
101
+ }
102
+ /** 创作者中心请求的 Cookie 头(上游 creator_cookie_str 的默认分支)。 */
103
+ export function cookieHeader(d) {
104
+ const all = [
105
+ ...d.jar.filter((c) => !WWW_ONLY.has(c.name)).map((c) => ({ name: c.name, value: c.value, domain: '.douyin.com' })),
106
+ ...d.creatorJar.map((c) => ({ name: c.name, value: c.value, domain: c.domain })),
107
+ ];
108
+ const used = new Set();
109
+ const out = [];
110
+ const take = (name, pred) => {
111
+ const i = all.findIndex((c, k) => !used.has(k) && c.name === name && pred(c));
112
+ if (i >= 0) {
113
+ used.add(i);
114
+ out.push(all[i]);
115
+ }
116
+ };
117
+ const hostOnly = (c) => c.domain === HOST;
118
+ const creatorDomain = (c) => c.domain.replace(/^\./, '') === HOST;
119
+ const shared = (c) => c.domain === '.douyin.com';
120
+ take('gd_random', hostOnly);
121
+ take('x-web-secsdk-uid', hostOnly);
122
+ take('gfkadpd', creatorDomain);
123
+ take('_tea_utm_cache_2906', creatorDomain);
124
+ take('csrf_session_id', hostOnly);
125
+ take('bd_ticket_guard_client_web_domain', shared);
126
+ take('s_v_web_id', hostOnly);
127
+ take('s_v_web_id', shared);
128
+ take('bd_ticket_guard_client_data', shared);
129
+ for (const [name] of d.cookies())
130
+ take(name, shared);
131
+ all.forEach((c, k) => {
132
+ if (!used.has(k))
133
+ out.push(c);
134
+ });
135
+ return out.map((c) => `${c.name}=${c.value}`).join('; ');
136
+ }
137
+ async function csrf(d, referer) {
138
+ const token = csrfTokens.get(d);
139
+ if (token)
140
+ return token;
141
+ return (await api.csrfToken(d, cookieHeader(d), CREATOR, CSRF_PROBE, referer)) ?? '';
142
+ }
143
+ /** creator 同源 XHR 的请求头(上游 _creator_xhr_headers)。 */
144
+ async function xhrHeaders(d, referer, options = {}) {
145
+ const h = new Headers();
146
+ for (const [k, v] of options.first ?? [])
147
+ h.set(k, v);
148
+ h.set('referer', referer).set('user-agent', PROFILE.ua).set('accept', 'application/json, text/plain, */*');
149
+ const token = await csrf(d, referer);
150
+ if (token)
151
+ h.set('x-secsdk-csrf-token', token);
152
+ if (options.contentType)
153
+ h.set('content-type', options.contentType);
154
+ h.set('accept-language', PROFILE.acceptLanguage);
155
+ if (options.bodyLength != null)
156
+ h.set('content-length', String(options.bodyLength));
157
+ h.set('cookie', cookieHeader(d));
158
+ if ((options.method ?? 'GET').toUpperCase() !== 'GET')
159
+ h.set('origin', CREATOR);
160
+ h.set('priority', 'u=1, i').set('sec-fetch-dest', 'empty').set('sec-fetch-mode', 'cors').set('sec-fetch-site', 'same-origin');
161
+ return h;
162
+ }
163
+ /** creator 的 query:初始参数 + 公共组 + msToken + a_bogus(上游 _creator_signed_params)。 */
164
+ async function signedParams(d, initial = [], options = {}) {
165
+ const { platform = true, msToken = true, sign = true, body = '' } = options;
166
+ const p = new Params().update(initial);
167
+ if (platform)
168
+ p.update(creatorPlatformParams());
169
+ if (msToken)
170
+ p.add('msToken', await d.msToken());
171
+ if (sign)
172
+ p.add('a_bogus', d.ab.sign(spliceUrl(p.pairs()), body, HOST));
173
+ return p.pairs();
174
+ }
175
+ /** 上游 _creator_api_request。 */
176
+ async function creatorApi(d, method, path, o = {}) {
177
+ const query = await signedParams(d, o.initial, { platform: o.platform, msToken: o.msToken, sign: o.sign, body: o.body ?? '' });
178
+ const bodyBytes = o.body == null ? undefined : Buffer.from(o.body, 'utf8');
179
+ const h = await xhrHeaders(d, o.referer ?? POST_VIDEO_REFERER, { contentType: o.contentType, method, first: o.first, bodyLength: bodyBytes?.length });
180
+ const res = await d.request({ method, url: CREATOR + path, headers: h.list(), query, body: bodyBytes });
181
+ try {
182
+ return jsonLoads(await res.text());
183
+ }
184
+ catch {
185
+ throw new SpecificAIError('UPSTREAM', `creator 接口返回的不是 JSON:${path}(HTTP ${res.status})`);
186
+ }
187
+ }
188
+ function ok(body, what) {
189
+ if (body.status_code != null && body.status_code !== 0)
190
+ throw new SpecificAIError('UPSTREAM', `${what} 失败:${body.status_msg ?? body.status_code}`, { detail: { status_code: body.status_code } });
191
+ return body;
192
+ }
193
+ /**
194
+ * V8 的 `Math.random().toString(36).substr(2)`(上游 _browser_random_s 用 node 一次取 64 个)。
195
+ * 确定性模式下是新起的一条 mulberry32(DEFAULT_SEED) 序列,与上游 node 子进程预加载的 Math.random 相同,不占用 rand 的主序列。
196
+ */
197
+ function browserRandomS(d) {
198
+ let pool = randomPools.get(d);
199
+ if (!pool?.length) {
200
+ const next = rand.isDeterministic() ? rand.mulberry32(rand.DEFAULT_SEED) : rand.random;
201
+ pool = Array.from({ length: 64 }, () => next().toString(36).substring(2));
202
+ randomPools.set(d, pool);
203
+ }
204
+ return pool.shift();
205
+ }
206
+ // ---------------------------------------------------------------- 上传
207
+ /** ImageX / VOD 的 STS(上游 get_image_upload_auth,接口 /web/api/media/upload/auth/v5/)。 */
208
+ export async function uploadAuth(d, referer = POST_VIDEO_REFERER) {
209
+ const h = await xhrHeaders(d, referer);
210
+ const query = await signedParams(d);
211
+ const body = jsonLoads(await (await d.request({ url: `${CREATOR}/web/api/media/upload/auth/v5/`, headers: h.list(), query })).text());
212
+ let sts;
213
+ try {
214
+ sts = jsonLoads(body.auth);
215
+ }
216
+ catch {
217
+ throw new SpecificAIError('UPSTREAM', `获取上传凭证失败:${body.status_msg ?? body.status_code ?? ''}`);
218
+ }
219
+ if (!sts.AccessKeyID || !sts.SecretAccessKey || !sts.SessionToken)
220
+ throw new SpecificAIError('UPSTREAM', '获取上传凭证失败:字段不完整');
221
+ return sts;
222
+ }
223
+ function gatewayHeaders(sign, contentType) {
224
+ const h = [];
225
+ if (sign['x-amz-content-sha256'])
226
+ h.push(['x-amz-content-sha256', sign['x-amz-content-sha256']]);
227
+ h.push(['x-amz-security-token', sign['x-amz-security-token']], ['x-amz-date', sign['x-amz-date']], ['referer', `${CREATOR}/`], ['authorization', sign.authorization], ['user-agent', PROFILE.ua]);
228
+ if (contentType)
229
+ h.push(['content-type', contentType]);
230
+ h.push(['accept', '*/*'], ['accept-language', PROFILE.acceptLanguage], ['origin', CREATOR], ['priority', 'u=1, i'], ['sec-fetch-dest', 'empty'], ['sec-fetch-mode', 'cors'], ['sec-fetch-site', 'cross-site']);
231
+ return h;
232
+ }
233
+ /** 一张图:ApplyImageUpload → TOS → CommitImageUpload(上游 upload_one_image)。 */
234
+ export async function uploadImage(d, sts, file, userId = '') {
235
+ const applyQuery = [
236
+ ['Action', 'ApplyImageUpload'],
237
+ ['Version', '2018-08-01'],
238
+ ['ServiceId', IMAGEX_SERVICE_ID],
239
+ ['app_id', IMAGEX_APP_ID],
240
+ ['user_id', userId],
241
+ ['s', browserRandomS(d)],
242
+ ];
243
+ const apply = jsonLoads(await (await d.plain({ url: `https://${IMAGEX_HOST}/`, headers: gatewayHeaders(sigv4(sts, 'GET', applyQuery)), query: applyQuery })).text());
244
+ const addr = apply.Result?.UploadAddress;
245
+ const store = addr?.StoreInfos?.[0];
246
+ if (!store)
247
+ throw new SpecificAIError('UPSTREAM', 'ApplyImageUpload 失败', { detail: { error: apply.ResponseMetadata?.Error ?? null } });
248
+ const up = jsonLoads(await (await d.plain({
249
+ method: 'POST',
250
+ url: `https://${addr.UploadHosts[0]}/upload/v1/${store.StoreUri}`,
251
+ headers: [
252
+ ['authorization', store.Auth],
253
+ ['referer', `${CREATOR}/`],
254
+ ['user-agent', PROFILE.ua],
255
+ ['x-storage-u', encodeURIComponent(userId)],
256
+ ['content-crc32', crc32Hex(file.data)],
257
+ ['content-type', 'application/octet-stream'],
258
+ ['content-disposition', 'attachment; filename="undefined"'],
259
+ ['accept', '*/*'],
260
+ ['accept-language', PROFILE.acceptLanguage],
261
+ ['origin', CREATOR],
262
+ ['sec-fetch-dest', 'empty'],
263
+ ['sec-fetch-mode', 'cors'],
264
+ ['sec-fetch-site', 'cross-site'],
265
+ ],
266
+ body: file.data,
267
+ })).text());
268
+ if (up.code !== 2000)
269
+ throw new SpecificAIError('UPSTREAM', `图片上传失败:${up.message ?? up.code}`);
270
+ const commitQuery = [
271
+ ['Action', 'CommitImageUpload'],
272
+ ['Version', '2018-08-01'],
273
+ ['ServiceId', IMAGEX_SERVICE_ID],
274
+ ['app_id', IMAGEX_APP_ID],
275
+ ['user_id', userId],
276
+ ];
277
+ const body = compactJson({ SessionKey: addr.SessionKey });
278
+ const commit = jsonLoads(await (await d.plain({ method: 'POST', url: `https://${IMAGEX_HOST}/`, headers: gatewayHeaders(sigv4(sts, 'POST', commitQuery, body), 'application/json'), query: commitQuery, body })).text());
279
+ const info = commit.Result?.PluginResult?.[0];
280
+ if (!info)
281
+ throw new SpecificAIError('UPSTREAM', 'CommitImageUpload 失败', { detail: { error: commit.ResponseMetadata?.Error ?? null } });
282
+ let width = Number(info.ImageWidth ?? 0) || 0;
283
+ let height = Number(info.ImageHeight ?? 0) || 0;
284
+ if (!width || !height)
285
+ ({ width, height } = imageSize(file.data));
286
+ return { uri: info.ImageUri, width, height };
287
+ }
288
+ /** 一个视频:ApplyUploadInner → TOS(直传或分片)→ 重新取 STS → CommitUploadInner(上游 upload_one_video / upload_prepared_video)。 */
289
+ export async function uploadVideo(d, sts, file, userId) {
290
+ const applyQuery = [
291
+ ['Action', 'ApplyUploadInner'],
292
+ ['Version', VOD_VERSION],
293
+ ['SpaceName', 'aweme'],
294
+ ['FileType', 'video'],
295
+ ['IsInner', 1],
296
+ ['FileSize', file.data.length],
297
+ ['app_id', IMAGEX_APP_ID],
298
+ ['user_id', userId],
299
+ ['s', browserRandomS(d)],
300
+ ];
301
+ const apply = jsonLoads(await (await d.plain({ url: `https://${VOD_HOST}/`, headers: gatewayHeaders(sigv4(sts, 'GET', applyQuery, '', 'vod')), query: applyQuery })).text());
302
+ const n = apply.Result?.InnerUploadAddress?.UploadNodes?.[0];
303
+ const store = n?.StoreInfos?.[0];
304
+ if (!store)
305
+ throw new SpecificAIError('UPSTREAM', 'ApplyUploadInner 失败', { detail: { error: apply.ResponseMetadata?.Error ?? null } });
306
+ const node = { store_uri: store.StoreUri, auth: store.Auth, upload_id: store.UploadID ?? '', upload_host: n.UploadHost, session_key: n.SessionKey, upload_header: n.UploadHeader ?? {} };
307
+ await tosUpload(d, CREATOR_TOS, node, file.data, userId);
308
+ const commitSts = await uploadAuth(d);
309
+ const commitQuery = [
310
+ ['Action', 'CommitUploadInner'],
311
+ ['Version', VOD_VERSION],
312
+ ['SpaceName', 'aweme'],
313
+ ['app_id', IMAGEX_APP_ID],
314
+ ['user_id', userId],
315
+ ];
316
+ const body = compactJson({ SessionKey: node.session_key, Functions: [{ name: 'GetMeta' }, { name: 'Snapshot', input: { SnapshotTime: 0 } }] });
317
+ const commit = jsonLoads(await (await d.plain({ method: 'POST', url: `https://${VOD_HOST}/`, headers: gatewayHeaders(sigv4(commitSts, 'POST', commitQuery, body, 'vod'), 'text/plain;charset=UTF-8'), query: commitQuery, body })).text());
318
+ const info = commit.Result?.Results?.[0];
319
+ if (!info)
320
+ throw new SpecificAIError('UPSTREAM', 'CommitUploadInner 失败', { detail: { error: commit.ResponseMetadata?.Error ?? null } });
321
+ const meta = info.SourceInfo ?? info.VideoMeta ?? {};
322
+ const vid = info.Vid ?? meta.Vid ?? '';
323
+ if (!vid)
324
+ throw new SpecificAIError('UPSTREAM', '提交后未拿到 vid');
325
+ return { vid, poster_uri: info.PosterUri ?? '', width: Number(meta.Width ?? 0) || 0, height: Number(meta.Height ?? 0) || 0, duration: Number(meta.Duration ?? 0) || 0, raw: info, commitSts };
326
+ }
327
+ /** 素材的访问地址(上游 get_creator_media_url)。 */
328
+ export async function mediaUrl(d, uri) {
329
+ const body = ok(await creatorApi(d, 'GET', '/aweme/v1/creator/get/url/', { initial: [['uri', uri]] }), 'creator/get/url');
330
+ const url = body.url?.url_list?.[0];
331
+ if (!url)
332
+ throw new SpecificAIError('UPSTREAM', 'creator/get/url 没有返回地址');
333
+ return url;
334
+ }
335
+ // ---------------------------------------------------------------- 作品管理
336
+ /**
337
+ * 自己的作品列表(上游 get_preview_video_list,发布页的作品预览,接口 /janus/douyin/creator/pc/work_list):
338
+ * 实录不带 msToken / a_bogus。上游只取第一页(max_cursor=0),翻页时换成上一页返回的 max_cursor。
339
+ */
340
+ export async function workList(d, maxCursor = 0) {
341
+ return creatorApi(d, 'GET', '/janus/douyin/creator/pc/work_list', {
342
+ initial: [
343
+ ['scene', 'star_atlas'],
344
+ ['device_platform', 'android'],
345
+ ['status', 4],
346
+ ['count', 18],
347
+ ['max_cursor', maxCursor],
348
+ ],
349
+ msToken: false,
350
+ sign: false,
351
+ });
352
+ }
353
+ // ---------------------------------------------------------------- 发布
354
+ /** 发布前必须齐备的安全素材(上游 _require_publish_security)。 */
355
+ export function requirePublishSecurity(d) {
356
+ const missing = [
357
+ ['ticket', d.tokens.ticket],
358
+ ['ts_sign', d.tokens.ts_sign],
359
+ ['private_key', d.privateKey],
360
+ ]
361
+ .filter(([, v]) => !v)
362
+ .map(([k]) => k);
363
+ if (missing.length)
364
+ throw authError(d.ctx, `发布需要 bd-ticket-guard 凭证(缺少 ${missing.join('、')}),请用扫码登录`);
365
+ if (!d.ticketMatchesSession())
366
+ throw authError(d.ctx, 'ticket / ts_sign 与 cookie 不属于同一次登录,请重新登录');
367
+ if (!d.dtraitBlob())
368
+ throw new SpecificAIError('AUTH_REQUIRED', `发布需要可按 create_v2 path 和当前时间重算的 dtrait 设备素材(${DTRAIT_BROKEN}),导入的成品 session_dtrait 不能用`, { hint: DTRAIT_HINT });
369
+ }
370
+ const jsLength = (s) => s.length;
371
+ /** 图文的 text 与 text_extra:标题段 type 7、分隔符 type 8(上游 _build_text_and_extra)。 */
372
+ function textAndExtra(title, desc) {
373
+ let text = '';
374
+ const extra = [];
375
+ if (title) {
376
+ text = title;
377
+ extra.push({ start: 0, end: jsLength(title), hashtag_id: 0, hashtag_name: '', type: 7 });
378
+ }
379
+ if (desc) {
380
+ if (text) {
381
+ const start = jsLength(text);
382
+ text += '。';
383
+ extra.push({ start, end: start + 1, hashtag_id: 0, hashtag_name: '', type: 8 });
384
+ }
385
+ text += desc;
386
+ }
387
+ return [text, extra];
388
+ }
389
+ /** mix_id / poi / hot_sentence 追加到 common 末尾(上游 build_*_create_item 的可选段)。 */
390
+ function withExtras(common, o, hotSentence) {
391
+ if (o.mixId)
392
+ common.mix_id = o.mixId;
393
+ if (o.poi) {
394
+ common.poi_id = o.poi.poi_id ?? '';
395
+ common.poi_name = o.poi.poi_name ?? '';
396
+ }
397
+ if (hotSentence && o.hotSpot)
398
+ common.hot_sentence = o.hotSpot.word ?? '';
399
+ }
400
+ /** 图文的 create_v2 item(上游 build_image_create_item)。 */
401
+ export function imageItem(images, o, creationId) {
402
+ const [text, extra] = textAndExtra(o.title ?? '', o.desc ?? '');
403
+ const cover = o.coverUri || images[Math.max(0, Math.min(o.coverIndex ?? 0, images.length - 1))].uri;
404
+ const common = {
405
+ text,
406
+ text_extra: compactJson(extra),
407
+ activity: '[]',
408
+ challenges: '[]',
409
+ hashtag_source: '',
410
+ mentions: '[]',
411
+ visibility_type: o.visibility,
412
+ download: o.allowDownload === false ? 0 : 1,
413
+ timing: o.timing ? Math.trunc(o.timing) : -1,
414
+ media_type: 2,
415
+ images: images.map((i) => ({ uri: i.uri, width: i.width, height: i.height })),
416
+ creation_id: creationId,
417
+ };
418
+ withExtras(common, o, true);
419
+ return { item: { common, cover: { poster: cover }, anchor: o.poi ? { poi: o.poi } : {} } };
420
+ }
421
+ /** 封面编辑器状态(上游 build_video_cover_tools_extend_info,没有推荐帧时的默认结构)。 */
422
+ export function coverToolsExtendInfo(posterUri) {
423
+ return {
424
+ recommendServerInfo: { res: [], times: [] },
425
+ recommendCoverList: [],
426
+ recommendCoverInfo: { isFromRecommend: false, isDefaultSelect: false, isRecommendClickFrom: '', selectInfo: {}, editingInfo: {} },
427
+ recommendCoverTime: 0,
428
+ coverInfo: { firstFrameCoverUri: posterUri, videoName: '', uri: posterUri, url: '', posterDelay: 0 },
429
+ coverUrl: '',
430
+ coverHorizontalInfo: null,
431
+ coverHorizontalUrl: '',
432
+ pasterInfo: null,
433
+ stateInfo: null,
434
+ croppedCoverInfo: null,
435
+ uploadBackgroundInfo: null,
436
+ uploadPasterInfo: null,
437
+ uploadCoverStateInfo: null,
438
+ xiguaCoverInfo: { posterDelay: 0 },
439
+ xiguaPasterInfo: null,
440
+ xiguaStateInfo: null,
441
+ xiguaUploadCoverStateInfo: null,
442
+ xiguaUploadBackgroundInfo: null,
443
+ xiguaUploadPasterInfo: null,
444
+ editXigua: false,
445
+ coverSource: '',
446
+ previewVideoList: [],
447
+ };
448
+ }
449
+ /** 视频的 create_v2 item(上游 build_video_create_item,封面编辑器状态用默认结构)。 */
450
+ export function videoItem(info, posterUri, o, creationId) {
451
+ const title = (o.title ?? '').trim();
452
+ const desc = (o.desc ?? '').trim();
453
+ const common = {
454
+ text: title ? `${title} ${desc}` : desc,
455
+ caption: desc,
456
+ item_title: title,
457
+ activity: '[]',
458
+ text_extra: '[]',
459
+ challenges: '[]',
460
+ mentions: '[]',
461
+ hashtag_source: '',
462
+ hot_sentence: o.hotSpot?.word ?? '',
463
+ interaction_stickers: '[]',
464
+ visibility_type: o.visibility,
465
+ download: o.allowDownload === false ? 0 : 1,
466
+ timing: o.timing ? Math.trunc(o.timing) : 0,
467
+ creation_id: creationId,
468
+ media_type: 4,
469
+ video_id: info.vid,
470
+ music_source: 0,
471
+ music_id: null,
472
+ };
473
+ withExtras(common, o, false);
474
+ const chapter = {
475
+ chapter_abstract: '',
476
+ chapter_details: [],
477
+ chapter_type: 1,
478
+ chapter_tools_info: {
479
+ chapter_recommend_detail: [],
480
+ chapter_recommend_abstract: '',
481
+ chapter_source: 2,
482
+ chapter_recommend_type: -2,
483
+ create_date: rand.nowSeconds(),
484
+ is_pc: '1',
485
+ is_pre_generated: '0',
486
+ is_syn: '1',
487
+ },
488
+ };
489
+ return {
490
+ item: {
491
+ common,
492
+ cover: {
493
+ cover_text_uri: null,
494
+ cover_text: null,
495
+ poster: posterUri,
496
+ poster_delay: 0,
497
+ cover_tools_extend_info: compactJson(coverToolsExtendInfo(posterUri)),
498
+ cover_tools_info: '{}',
499
+ },
500
+ mix: {},
501
+ selected_member: { is_selected_member_video: false },
502
+ chapter: { chapter: compactJson(chapter) },
503
+ anchor: o.poi ? { poi: o.poi } : {},
504
+ sync: { should_sync: false, sync_to_toutiao: 0 },
505
+ open_platform: {},
506
+ assistant: { is_preview: 0, is_post_assistant: 1 },
507
+ },
508
+ };
509
+ }
510
+ /** POST create_v2(上游 _create_aweme)。 */
511
+ export async function createAweme(d, item, referer = POST_IMAGE_REFERER) {
512
+ const path = '/web/api/media/aweme/create_v2/';
513
+ requirePublishSecurity(d);
514
+ const bd = new Headers();
515
+ await d.withBd(bd, path, { aid: READ_AID, origin: CREATOR, requireDtrait: true });
516
+ const body = compactJson(item);
517
+ const bodyBytes = Buffer.from(body, 'utf8');
518
+ const token = await csrf(d, referer);
519
+ if (!token)
520
+ throw new SpecificAIError('UPSTREAM', 'create_v2 缺少 x-secsdk-csrf-token,请求未发送');
521
+ const g = (k) => bd.get(k);
522
+ const headers = [
523
+ ['content-length', String(bodyBytes.length)],
524
+ ['x-tt-session-dtrait', g('x-tt-session-dtrait')],
525
+ ['bd-ticket-guard-web-version', g('bd-ticket-guard-web-version')],
526
+ ['bd-ticket-guard-client-data', g('bd-ticket-guard-client-data')],
527
+ ['bd-ticket-guard-web-sign-type', g('bd-ticket-guard-web-sign-type')],
528
+ ['user-agent', PROFILE.ua],
529
+ ['accept', 'application/json, text/plain, */*'],
530
+ ['x-secsdk-csrf-token', token],
531
+ ['content-type', 'application/json'],
532
+ ['bd-ticket-guard-ree-public-key', g('bd-ticket-guard-ree-public-key')],
533
+ ['bd-ticket-guard-version', g('bd-ticket-guard-version')],
534
+ ['origin', CREATOR],
535
+ ['sec-fetch-site', 'same-origin'],
536
+ ['sec-fetch-mode', 'cors'],
537
+ ['sec-fetch-dest', 'empty'],
538
+ ['referer', referer],
539
+ ['accept-encoding', 'gzip, deflate, br, zstd'],
540
+ ['accept-language', PROFILE.acceptLanguage],
541
+ ['cookie', cookieHeader(d)],
542
+ ['priority', 'u=1, i'],
543
+ ];
544
+ const p = new Params().add('read_aid', READ_AID).update(creatorPlatformParams());
545
+ p.add('msToken', await d.msToken());
546
+ p.add('a_bogus', d.ab.sign(spliceUrl(p.pairs()), body, HOST));
547
+ const res = await d.request({ method: 'POST', url: CREATOR + path, headers, query: p.pairs(), body: bodyBytes });
548
+ return riskJson(res);
549
+ }
550
+ /** 前端 creationId:8 位小写字母数字 + 毫秒时间戳。 */
551
+ export const creationId = () => rand.string(8, 'abcdefghijklmnopqrstuvwxyz0123456789') + String(rand.now());
552
+ /** 发布图文(上游 post_images):每张图之前重新取一份 STS。 */
553
+ export async function postImages(d, images, o) {
554
+ requirePublishSecurity(d);
555
+ if (!images.length)
556
+ throw new SpecificAIError('USAGE', '图文至少需要一张 --image');
557
+ await bootstrap(d);
558
+ const infos = [];
559
+ for (const [i, img] of images.entries()) {
560
+ const sts = await uploadAuth(d, POST_IMAGE_REFERER);
561
+ infos.push(await uploadImage(d, sts, img, ''));
562
+ d.ctx.log.info(`图片上传成功 [${i + 1}/${images.length}]`);
563
+ }
564
+ return createAweme(d, imageItem(infos, o, creationId()));
565
+ }
566
+ /** 上传用的 uid(上游 _resolve_user_id 直接调 get_my_uid)。 */
567
+ async function creatorUid(d) {
568
+ try {
569
+ return await api.myUid(d);
570
+ }
571
+ catch (err) {
572
+ d.ctx.log.warn(`获取 uid 失败,X-Storage-U 置空:${err.message}`);
573
+ return '';
574
+ }
575
+ }
576
+ /**
577
+ * 发布视频(上游 post_video)。给了封面就先传封面(`tos-` 开头的 uri 直接用);没给封面时用 VOD Snapshot 抽的首帧,
578
+ * 即上游传入 cover_tools_extend_info 时的分支——上游默认的 AI 封面链依赖 OpenCV / PyAV 抽帧,Node 下不做。
579
+ */
580
+ export async function postVideo(d, video, cover, o) {
581
+ requirePublishSecurity(d);
582
+ await bootstrap(d);
583
+ const userId = await creatorUid(d);
584
+ const cid = creationId();
585
+ await workList(d);
586
+ ok(await creatorApi(d, 'GET', '/aweme/v1/cover/gen/ref/', { initial: [['creation_id', cid]] }), 'cover/gen/ref');
587
+ const feats = compactJson({ has_ai_metadata: false, is_xing_tu_submit: false, has_marketing_poi: false, item_type: 'video' });
588
+ ok(await creatorApi(d, 'GET', '/aweme/v3/user_declaration/suggestion/', {
589
+ initial: [
590
+ ['scene', 'new_self_media_before_publish'],
591
+ ['creation_id', cid],
592
+ ['user_decl_judge_feats', feats],
593
+ ['libra_token', 'douyin_pc'],
594
+ ],
595
+ }), 'user_declaration/suggestion');
596
+ const sts = await uploadAuth(d);
597
+ const info = await uploadVideo(d, sts, video, userId);
598
+ d.ctx.log.info(`视频上传成功 ${info.width}x${info.height} ${info.duration.toFixed(1)}s`);
599
+ const poster = typeof cover === 'string' ? cover : cover ? (await uploadImage(d, info.commitSts, cover, userId)).uri : info.poster_uri;
600
+ return createAweme(d, videoItem(info, poster, o, cid), POST_VIDEO_REFERER);
601
+ }