@iflyrpa/actions 4.1.0-beta.5 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="19f291bd-1860-5dc2-abcf-dd482b04a403")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="309d0c4f-ece2-5152-b000-0390cb565a64")}catch(e){}}();
3
3
  import * as __WEBPACK_EXTERNAL_MODULE_crypto__ from "crypto";
4
4
  import * as __WEBPACK_EXTERNAL_MODULE_fs__ from "fs";
5
5
  import * as __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_package_json_58ae5f06__ from "@iflyrpa/share/package.json";
@@ -4352,7 +4352,9 @@ function __webpack_require__(moduleId) {
4352
4352
  return module;
4353
4353
  };
4354
4354
  })();
4355
- var package_namespaceObject = JSON.parse('{"i8":"4.1.0-beta.5"}');
4355
+ var package_namespaceObject = {
4356
+ i8: "4.1.0"
4357
+ };
4356
4358
  const USER_MESSAGE = {
4357
4359
  PROXY_UNAVAILABLE: "代理暂时不可用,请稍后重试或使用本地IP",
4358
4360
  NETWORK_ERROR: "网络异常,请稍后重试",
@@ -4386,6 +4388,11 @@ function classifyPublishError(handledError) {
4386
4388
  const RPA_ERROR_WEBHOOK_URL = "https://open.xfchat.iflytek.com/open-apis/bot/v2/hook/d202c0dc-5af5-40bc-83ed-abc677caa4a5";
4387
4389
  const ALARM_THROTTLE_MS = 60000;
4388
4390
  const lastSentAt = new Map();
4391
+ const THROTTLE_MAP_MAX_KEYS = 500;
4392
+ const pruneThrottleMap = (now)=>{
4393
+ if (lastSentAt.size < THROTTLE_MAP_MAX_KEYS) return;
4394
+ for (const [key, at] of lastSentAt)if (now - at >= ALARM_THROTTLE_MS) lastSentAt.delete(key);
4395
+ };
4389
4396
  const postFeishuWebhook = (webhookUrl, payload)=>__WEBPACK_EXTERNAL_MODULE_axios__["default"].post(webhookUrl, payload, {
4390
4397
  headers: {
4391
4398
  "Content-Type": "application/json"
@@ -4463,10 +4470,11 @@ const buildFeishuPostMessage = (report)=>{
4463
4470
  };
4464
4471
  const reportFeishuAlarm = (report)=>{
4465
4472
  try {
4466
- const key = `${report.platform}|${report.source}|${report.errorType}|${report.code ?? ""}`;
4473
+ const key = `${report.platform}|${report.source}|${report.stage}|${report.errorType}|${report.code ?? ""}`;
4467
4474
  const now = Date.now();
4468
4475
  const last = lastSentAt.get(key);
4469
4476
  if (last && now - last < ALARM_THROTTLE_MS) return;
4477
+ pruneThrottleMap(now);
4470
4478
  lastSentAt.set(key, now);
4471
4479
  postFeishuWebhook(RPA_ERROR_WEBHOOK_URL, buildFeishuPostMessage(report)).catch(()=>{});
4472
4480
  } catch {}
@@ -4612,6 +4620,19 @@ const ALARM_STATUS = new Set([
4612
4620
  461,
4613
4621
  471
4614
4622
  ]);
4623
+ function isLocalAddress(url) {
4624
+ let hostname;
4625
+ try {
4626
+ hostname = new URL(url).hostname.toLowerCase();
4627
+ } catch {
4628
+ return false;
4629
+ }
4630
+ if ("localhost" === hostname || "::1" === hostname || "[::1]" === hostname) return true;
4631
+ if (hostname.endsWith(".localhost") || hostname.endsWith(".local")) return true;
4632
+ if (/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(hostname)) return true;
4633
+ if ("0.0.0.0" === hostname) return true;
4634
+ return false;
4635
+ }
4615
4636
  const HTTP_STATUS_MESSAGE = {
4616
4637
  400: "请求参数错误,请检查参数格式是否正确!",
4617
4638
  401: "登录状态已失效,请重新登录后重试!",
@@ -4648,7 +4669,14 @@ const HTTP_STATUS_MESSAGE = {
4648
4669
  };
4649
4670
  class Http {
4650
4671
  static handleApiError(error) {
4651
- if (error && "object" == typeof error && "code" in error && "message" in error) return error;
4672
+ if (error && "object" == typeof error && "code" in error && "message" in error) {
4673
+ const resp = error;
4674
+ if ("string" != typeof resp.message || !resp.message.trim()) return {
4675
+ ...resp,
4676
+ message: USER_MESSAGE.SYSTEM_ERROR
4677
+ };
4678
+ return resp;
4679
+ }
4652
4680
  return {
4653
4681
  code: 500,
4654
4682
  message: USER_MESSAGE.SYSTEM_ERROR,
@@ -4786,25 +4814,47 @@ class Http {
4786
4814
  errorResponse.message = message;
4787
4815
  }
4788
4816
  if (error.message.includes("Proxy connection ended")) errorResponse.message = "所在区域代理连接超时,请更换区域或稍后重试!";
4789
- const status = error.response?.status;
4790
- reportFeishuAlarm({
4791
- level: status && ALARM_STATUS.has(status) ? "alarm" : "warning",
4792
- platform: this.platform || "unknown",
4793
- source: "http",
4794
- stage: `${(error.config?.method || "get").toUpperCase()} ${error.config?.url || "-"}`,
4795
- errorType: status ? `HTTP_${status}` : error.code || "NETWORK_ERROR",
4796
- code: errorResponse.code,
4797
- msg: errorResponse.message,
4798
- url: error.config?.url,
4799
- title: "RPA接口异常"
4800
- });
4817
+ errorResponse.extra = {
4818
+ ...errorResponse.extra,
4819
+ alarmStatus: error.response?.status,
4820
+ alarmErrorCode: error.code
4821
+ };
4801
4822
  throw errorResponse;
4802
4823
  });
4803
4824
  }
4825
+ reportRequestFailure(config, error, attempts) {
4826
+ const status = error.extra?.alarmStatus;
4827
+ const axiosCode = error.extra?.alarmErrorCode;
4828
+ const method = (config.method || "get").toUpperCase();
4829
+ const baseURL = config.baseURL || this.apiClient.defaults.baseURL || "";
4830
+ const fullUrl = config.url ? /^https?:\/\//.test(config.url) ? config.url : `${baseURL.replace(/\/$/, "")}${config.url}` : baseURL;
4831
+ if (!fullUrl) {
4832
+ this.logger?.debug(`[告警跳过] 请求失败但无 URL,不上报: ${error.message}`);
4833
+ return;
4834
+ }
4835
+ if (isLocalAddress(fullUrl)) {
4836
+ this.logger?.debug(`[告警跳过] 本机地址不上报: ${fullUrl}`);
4837
+ return;
4838
+ }
4839
+ const rawMsg = "string" == typeof error.message && error.message.trim() ? error.message : "";
4840
+ const baseMsg = rawMsg || `请求失败(无错误文案,code=${error.code ?? "unknown"})`;
4841
+ const retriedSuffix = attempts > 0 ? `(已重试${attempts}次仍失败)` : "";
4842
+ reportFeishuAlarm({
4843
+ level: status && ALARM_STATUS.has(status) ? "alarm" : "warning",
4844
+ platform: "@iflyrpa/playwright",
4845
+ source: "http",
4846
+ stage: `${method} ${fullUrl}`,
4847
+ errorType: status ? `HTTP_${status}` : axiosCode || "NETWORK_ERROR",
4848
+ code: error.code,
4849
+ msg: `${baseMsg}${retriedSuffix}`,
4850
+ url: fullUrl,
4851
+ title: "RPA接口异常"
4852
+ });
4853
+ }
4804
4854
  async api(config, options) {
4805
4855
  const retries = options?.retries ?? 0;
4806
4856
  const retryDelay = options?.retryDelay ?? 500;
4807
- const reqTimeout = options?.timeout ?? 30000;
4857
+ const reqTimeout = options?.timeout ?? 60000;
4808
4858
  const externalSignal = options?.signal;
4809
4859
  let agent;
4810
4860
  const sessionRt = async (Rtimes)=>{
@@ -4845,10 +4895,12 @@ class Http {
4845
4895
  ].includes(handledError.code);
4846
4896
  if (Rtimes < retries && isRetry) {
4847
4897
  const url = config.url || "";
4848
- this.logger?.warn(`进入第${Rtimes + 1}次重试!错误码: ${handledError.code}, 请求地址: ${url}`);
4849
- await new Promise((resolve)=>setTimeout(resolve, retryDelay));
4898
+ const backoff = Math.min(retryDelay * 2 ** Rtimes, 5000);
4899
+ this.logger?.warn(`进入第${Rtimes + 1}次重试!错误码: ${handledError.code}, 等待: ${backoff}ms, 请求地址: ${url}`);
4900
+ await new Promise((resolve)=>setTimeout(resolve, backoff));
4850
4901
  return sessionRt(Rtimes + 1);
4851
4902
  }
4903
+ this.reportRequestFailure(config, handledError, Rtimes);
4852
4904
  return Promise.reject(handledError);
4853
4905
  }
4854
4906
  };
@@ -5962,7 +6014,7 @@ const cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-
5962
6014
  const cidrv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/;
5963
6015
  const regexes_base64 = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/;
5964
6016
  const regexes_base64url = /^[A-Za-z0-9_-]*$/;
5965
- const hostname = /^(?=.{1,253}\.?$)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?)*\.?$/;
6017
+ const regexes_hostname = /^(?=.{1,253}\.?$)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?)*\.?$/;
5966
6018
  const e164 = /^\+(?:[0-9]){6,14}[0-9]$/;
5967
6019
  const dateSource = "(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))";
5968
6020
  const regexes_date = /*@__PURE__*/ new RegExp(`^${dateSource}$`);
@@ -6558,7 +6610,7 @@ const $ZodURL = /*@__PURE__*/ $constructor("$ZodURL", (inst, def)=>{
6558
6610
  code: "invalid_format",
6559
6611
  format: "url",
6560
6612
  note: "Invalid hostname",
6561
- pattern: hostname.source,
6613
+ pattern: regexes_hostname.source,
6562
6614
  input: payload.value,
6563
6615
  inst,
6564
6616
  continue: !def.abort
@@ -8569,6 +8621,14 @@ function schemas_object(shape, params) {
8569
8621
  };
8570
8622
  return new ZodObject(def);
8571
8623
  }
8624
+ function looseObject(shape, params) {
8625
+ return new ZodObject({
8626
+ type: "object",
8627
+ shape,
8628
+ catchall: unknown(),
8629
+ ...normalizeParams(params)
8630
+ });
8631
+ }
8572
8632
  const ZodUnion = /*@__PURE__*/ $constructor("ZodUnion", (inst, def)=>{
8573
8633
  $ZodUnion.init(inst, def);
8574
8634
  ZodType.init(inst, def);
@@ -9261,7 +9321,7 @@ const mockAction = async (task, params)=>{
9261
9321
  ".png"
9262
9322
  ].includes(ext)) throw {
9263
9323
  code: 414,
9264
- message: `图片格式不支持:${fileName}。百家号仅支持 jpg、png 格式,请转换后重试。`,
9324
+ message: "图片格式不支持,百家号仅支持 jpg、png 格式,请转换后重试。",
9265
9325
  data: ""
9266
9326
  };
9267
9327
  const image = await (0, __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.downloadImage)(url, __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(tmpCachePath, fileName));
@@ -9942,7 +10002,8 @@ const XhsFansExport = async (_task, params)=>{
9942
10002
  },
9943
10003
  _task.logger,
9944
10004
  params.proxyLoc,
9945
- params.accountId
10005
+ params.accountId,
10006
+ "xiaohongshu"
9946
10007
  ];
9947
10008
  const http = new Http(...args);
9948
10009
  const fans = {
@@ -9956,8 +10017,8 @@ const XhsFansExport = async (_task, params)=>{
9956
10017
  url: "https://creator.xiaohongshu.com/api/galaxy/creator/home/personal_info"
9957
10018
  }, {
9958
10019
  retries: 3,
9959
- retryDelay: 20,
9960
- timeout: 3000
10020
+ retryDelay: 300,
10021
+ timeout: 30000
9961
10022
  });
9962
10023
  fans.fans_count = Number(res.data.fans_count);
9963
10024
  fans.digg_count = Number(res.data.faved_count);
@@ -11094,7 +11155,8 @@ const XhsSessionCheck = async (_task, params)=>{
11094
11155
  },
11095
11156
  _task.logger,
11096
11157
  params.proxyLoc,
11097
- params.accountId
11158
+ params.accountId,
11159
+ "xiaohongshu"
11098
11160
  ];
11099
11161
  const http = new Http(...args);
11100
11162
  http.addResponseInterceptor((response)=>{
@@ -11142,8 +11204,8 @@ const XhsSessionCheck = async (_task, params)=>{
11142
11204
  headers: loginBaseXsHeader
11143
11205
  }, {
11144
11206
  retries: 3,
11145
- retryDelay: 20,
11146
- timeout: 5000
11207
+ retryDelay: 300,
11208
+ timeout: 30000
11147
11209
  }).catch((e)=>{
11148
11210
  const clientTimestamp = Date.now();
11149
11211
  const serverDate = e?.extra?.serverDate;
@@ -11175,8 +11237,8 @@ const XhsSessionCheck = async (_task, params)=>{
11175
11237
  headers: webSessionXsHeader
11176
11238
  }, {
11177
11239
  retries: 3,
11178
- retryDelay: 20,
11179
- timeout: 5000
11240
+ retryDelay: 300,
11241
+ timeout: 30000
11180
11242
  });
11181
11243
  const [baseInfo, web_session] = await Promise.all([
11182
11244
  _baseInfo,
@@ -11268,6 +11330,10 @@ const ShipinhaoSessionCheck = async (_task, params)=>{
11268
11330
  };
11269
11331
  return (0, __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.success)(data, message);
11270
11332
  };
11333
+ const extractEncFileKey = (downloadUrl)=>{
11334
+ if (!downloadUrl) return "";
11335
+ return downloadUrl.split("encfilekey=")[1]?.split("&")[0] || "";
11336
+ };
11271
11337
  const rid = ()=>`${Math.floor(Date.now() / 1e3).toString(16)}-${[
11272
11338
  ...Array(8)
11273
11339
  ].map(()=>Math.floor(16 * Math.random()).toString(16)).join("")}`;
@@ -15422,7 +15488,8 @@ const getXhsUnreadCount = async (_task, params)=>{
15422
15488
  },
15423
15489
  _task.logger,
15424
15490
  params.proxyLoc,
15425
- params.accountId
15491
+ params.accountId,
15492
+ "xiaohongshu"
15426
15493
  ];
15427
15494
  const http = new Http(...args);
15428
15495
  let unreadCount = {
@@ -15451,8 +15518,8 @@ const getXhsUnreadCount = async (_task, params)=>{
15451
15518
  headers: xsHeader
15452
15519
  }, {
15453
15520
  retries: 3,
15454
- retryDelay: 20,
15455
- timeout: 3000
15521
+ retryDelay: 300,
15522
+ timeout: 30000
15456
15523
  });
15457
15524
  const isSuccess = 0 === res.code;
15458
15525
  if (isSuccess) unreadCount = res.data;
@@ -15863,7 +15930,8 @@ async function getXiaohongshuData(_task, params) {
15863
15930
  },
15864
15931
  _task.logger,
15865
15932
  params.proxyLoc,
15866
- params.accountId
15933
+ params.accountId,
15934
+ "xiaohongshu"
15867
15935
  ];
15868
15936
  const http = new Http(...args);
15869
15937
  const xsEncrypt = new Xhshow();
@@ -15879,8 +15947,8 @@ async function getXiaohongshuData(_task, params) {
15879
15947
  url: "https://creator.xiaohongshu.com/api/galaxy/creator/home/personal_info"
15880
15948
  }, {
15881
15949
  retries: 3,
15882
- retryDelay: 20,
15883
- timeout: 3000
15950
+ retryDelay: 300,
15951
+ timeout: 30000
15884
15952
  }),
15885
15953
  http.api({
15886
15954
  method: "get",
@@ -15889,8 +15957,8 @@ async function getXiaohongshuData(_task, params) {
15889
15957
  headers: sevenDataXsHeader
15890
15958
  }, {
15891
15959
  retries: 3,
15892
- retryDelay: 20,
15893
- timeout: 3000
15960
+ retryDelay: 300,
15961
+ timeout: 30000
15894
15962
  })
15895
15963
  ]);
15896
15964
  const xhsData = {
@@ -18750,7 +18818,7 @@ async function publishDynamic(cookies, uin, fingerPrintDeviceId, aId, proxyHttp,
18750
18818
  name: params.music.name,
18751
18819
  artist: params.music.authorName,
18752
18820
  mediaStreamingUrl: params.music.url,
18753
- docType: params.music.raw.playableInfo.type
18821
+ docType: 2 === params.music.raw.bgmSource ? 0 : 1
18754
18822
  },
18755
18823
  groupId: params.music.id,
18756
18824
  hasBgm: 1,
@@ -18927,7 +18995,7 @@ const shipinhaoPublish_mock_mockAction = async (task, params)=>{
18927
18995
  const resultMsg = publishResult.data?.baseResp?.errmsg ?? publishResult.errMsg;
18928
18996
  if (0 === resultCode) {
18929
18997
  task.logger.info("[shipinhaoPublish] 发布成功");
18930
- const publishId = uploadedImages[0]?.thumbUrl?.split("encfilekey=")[1]?.split("&")[0] || "";
18998
+ const publishId = extractEncFileKey(uploadedImages[0]?.thumbUrl);
18931
18999
  await updateTaskState?.({
18932
19000
  state: __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.TaskState.SUCCESS,
18933
19001
  result: {
@@ -19719,13 +19787,110 @@ async function getShipinhaoUploadAuth(cookies, http) {
19719
19787
  pictureFileType
19720
19788
  };
19721
19789
  }
19790
+ const MENTION_TEXT_SUFFIX = "\u0020";
19791
+ const MENTION_XML_SUFFIX = "\u2005";
19792
+ function payload_buildDescription(params) {
19793
+ let description = params.description || "";
19794
+ for (const topic of params.topics || [])description += `#${topic}`;
19795
+ for (const user of params.mentionedUsers || [])description += `@${user.nickname}${MENTION_TEXT_SUFFIX}`;
19796
+ return description;
19797
+ }
19798
+ function buildMentionedUser(mentionedUsers) {
19799
+ return (mentionedUsers || []).map((user)=>({
19800
+ nickname: `${user.nickname}${MENTION_TEXT_SUFFIX}`
19801
+ }));
19802
+ }
19803
+ function payload_buildTopicXml(params) {
19804
+ const values = [];
19805
+ let atIndex = null;
19806
+ if (params.description) values.push(`<![CDATA[${params.description}]]>`);
19807
+ for (const topic of params.topics || [])values.push(`<topic><![CDATA[#${topic}#]]></topic>`);
19808
+ for (const user of params.mentionedUsers || []){
19809
+ if (null === atIndex) atIndex = values.length;
19810
+ values.push(`<![CDATA[@${user.nickname}${MENTION_XML_SUFFIX}]]>`);
19811
+ }
19812
+ let xml = "<finder>";
19813
+ xml += "<version>1</version>";
19814
+ xml += `<valuecount>${values.length}</valuecount>`;
19815
+ xml += `<style><at>${atIndex ?? ""}</at></style>`;
19816
+ values.forEach((value, index)=>{
19817
+ xml += `<value${index}>${value}</value${index}>`;
19818
+ });
19819
+ xml += "</finder>";
19820
+ return xml;
19821
+ }
19822
+ function payload_buildLocation(location) {
19823
+ if (!location) return {
19824
+ latitude: 0,
19825
+ longitude: 0,
19826
+ city: "",
19827
+ poiName: "",
19828
+ address: "",
19829
+ poiClassifyId: ""
19830
+ };
19831
+ return {
19832
+ latitude: location.latitude,
19833
+ longitude: location.longitude,
19834
+ city: location.city,
19835
+ poiName: location.poiName || "",
19836
+ address: location.address || "",
19837
+ poiClassifyId: location.poiClassifyId || ""
19838
+ };
19839
+ }
19840
+ function buildTopic(params) {
19841
+ const topic = {
19842
+ finderTopicInfo: payload_buildTopicXml(params)
19843
+ };
19844
+ if (params.collection) {
19845
+ topic.collectionId = params.collection.collectionId;
19846
+ topic.collectionName = params.collection.collectionName;
19847
+ }
19848
+ return topic;
19849
+ }
19850
+ function buildEvent(event) {
19851
+ if (!event) return {};
19852
+ return {
19853
+ eventTopicId: event.eventTopicId,
19854
+ eventName: event.eventName,
19855
+ eventCreatorNickname: event.eventCreatorNickname || ""
19856
+ };
19857
+ }
19858
+ function buildExtReading(link) {
19859
+ if (!link) return {
19860
+ link: "",
19861
+ title: "",
19862
+ urlType: 1
19863
+ };
19864
+ return {
19865
+ link: link.link.replace(/[\s\u200b]/g, ""),
19866
+ title: link.title,
19867
+ urlType: link.urlType ?? 1
19868
+ };
19869
+ }
19870
+ function buildTagInfo(tagInfo, tagKey) {
19871
+ return {
19872
+ ...tagInfo,
19873
+ tagKey
19874
+ };
19875
+ }
19722
19876
  const CHUNK_SIZE = 8388608;
19877
+ const UPLOAD_STAGE_TIMEOUT = 180000;
19878
+ const DEFAULT_TUNING = {
19879
+ metaTimeout: UPLOAD_STAGE_TIMEOUT,
19880
+ partTimeout: UPLOAD_STAGE_TIMEOUT,
19881
+ partRetries: 3,
19882
+ completeTimeout: UPLOAD_STAGE_TIMEOUT
19883
+ };
19723
19884
  async function uploader_uploadFile(opts) {
19724
- const { filePath, fileType, uin, authKey, http, logger } = opts;
19885
+ const { filePath, fileType, uin, authKey, http, logger, tuning } = opts;
19725
19886
  const stat = __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__["default"].statSync(filePath);
19726
19887
  const fileSize = stat.size;
19727
19888
  const fileName = filePath.split(/[\\/]/).pop() || "file";
19728
- logger?.info(`开始上传: ${fileName} (${fileSize} B, filetype=${fileType})`);
19889
+ const metaTimeout = tuning?.metaTimeout ?? DEFAULT_TUNING.metaTimeout;
19890
+ const partTimeout = tuning?.partTimeout ?? DEFAULT_TUNING.partTimeout;
19891
+ const partRetries = tuning?.partRetries ?? DEFAULT_TUNING.partRetries;
19892
+ const completeTimeout = tuning?.completeTimeout ?? DEFAULT_TUNING.completeTimeout;
19893
+ logger?.info(`[shipinhaoPublishVideo] 开始上传: ${fileName} (${fileSize} B, filetype=${fileType})`);
19729
19894
  const fileMd5 = await computeFileMd5(filePath);
19730
19895
  const taskId = generateTaskId(fileName, fileSize, fileMd5);
19731
19896
  const baseUrl = "https://finderassistancea.video.qq.com";
@@ -19736,7 +19901,7 @@ async function uploader_uploadFile(opts) {
19736
19901
  const chunkCount = Math.ceil(fileSize / CHUNK_SIZE);
19737
19902
  const blockPartLength = [];
19738
19903
  for(let i = 0; i < chunkCount; i++)blockPartLength.push(Math.min((i + 1) * CHUNK_SIZE, fileSize));
19739
- logger?.info(`申请 UploadID: ${chunkCount} 片`);
19904
+ logger?.info(`[shipinhaoPublishVideo] 视频分片: ${chunkCount} 片`);
19740
19905
  const applyRes = await http.api({
19741
19906
  method: "PUT",
19742
19907
  url: `${baseUrl}/applyuploaddfs`,
@@ -19748,12 +19913,15 @@ async function uploader_uploadFile(opts) {
19748
19913
  BlockSum: chunkCount,
19749
19914
  BlockPartLength: blockPartLength
19750
19915
  }
19916
+ }, {
19917
+ timeout: metaTimeout,
19918
+ retries: 2,
19919
+ retryDelay: 2000
19751
19920
  });
19752
19921
  if (!applyRes.UploadID && !applyRes.ListPartsResult) throw new Error("申请 UploadID 失败: " + JSON.stringify(applyRes));
19753
19922
  let uploadId = applyRes.UploadID;
19754
19923
  const uploadedParts = new Set();
19755
19924
  if (applyRes.ListPartsResult) {
19756
- logger?.info("检测到已上传分片,执行续传");
19757
19925
  const parts = Array.isArray(applyRes.ListPartsResult.Part) ? applyRes.ListPartsResult.Part : applyRes.ListPartsResult.Part ? [
19758
19926
  applyRes.ListPartsResult.Part
19759
19927
  ] : [];
@@ -19769,6 +19937,10 @@ async function uploader_uploadFile(opts) {
19769
19937
  BlockSum: chunkCount,
19770
19938
  BlockPartLength: blockPartLength
19771
19939
  }
19940
+ }, {
19941
+ timeout: metaTimeout,
19942
+ retries: 2,
19943
+ retryDelay: 2000
19772
19944
  });
19773
19945
  uploadId = retryRes.UploadID;
19774
19946
  if (!uploadId) throw new Error("续传获取 UploadID 失败");
@@ -19785,7 +19957,6 @@ async function uploader_uploadFile(opts) {
19785
19957
  PartNumber: partNumber,
19786
19958
  ETag: existing.ETag
19787
19959
  });
19788
- logger?.info(`分片 ${partNumber}/${chunkCount} 已存在,跳过`);
19789
19960
  continue;
19790
19961
  }
19791
19962
  }
@@ -19795,6 +19966,7 @@ async function uploader_uploadFile(opts) {
19795
19966
  const chunk = Buffer.alloc(chunkSize);
19796
19967
  __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__["default"].readSync(fd, chunk, 0, chunkSize, start);
19797
19968
  const chunkMd5 = __WEBPACK_EXTERNAL_MODULE_node_crypto_9ba42079__["default"].createHash("md5").update(chunk).digest("hex");
19969
+ const partStart = Date.now();
19798
19970
  await http.api({
19799
19971
  method: "PUT",
19800
19972
  url: `${baseUrl}/uploadpartdfs?PartNumber=${partNumber}&UploadID=${encodeURIComponent(uploadId)}`,
@@ -19804,17 +19976,20 @@ async function uploader_uploadFile(opts) {
19804
19976
  "Content-MD5": chunkMd5
19805
19977
  },
19806
19978
  data: chunk
19979
+ }, {
19980
+ timeout: partTimeout,
19981
+ retries: partRetries,
19982
+ retryDelay: 2000
19807
19983
  });
19984
+ Date.now();
19808
19985
  partInfo.push({
19809
19986
  PartNumber: partNumber,
19810
19987
  ETag: `"${chunkMd5}"`
19811
19988
  });
19812
- logger?.info(`分片 ${partNumber}/${chunkCount} 完成 (${chunkSize} B)`);
19813
19989
  }
19814
19990
  } finally{
19815
19991
  __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__["default"].closeSync(fd);
19816
19992
  }
19817
- logger?.info("合并分片...");
19818
19993
  const completeRes = await http.api({
19819
19994
  method: "POST",
19820
19995
  url: `${baseUrl}/completepartuploaddfs?UploadID=${encodeURIComponent(uploadId)}`,
@@ -19826,9 +20001,13 @@ async function uploader_uploadFile(opts) {
19826
20001
  TransFlag: "0_0",
19827
20002
  PartInfo: partInfo
19828
20003
  }
20004
+ }, {
20005
+ timeout: completeTimeout,
20006
+ retries: 1,
20007
+ retryDelay: 3000
19829
20008
  });
19830
20009
  if (!completeRes.DownloadURL) throw new Error("合并分片失败: " + JSON.stringify(completeRes));
19831
- logger?.info(`上传成功: ${completeRes.DownloadURL.slice(0, 80)}...`);
20010
+ logger?.info(`[shipinhaoPublishVideo] ${fileName} 上传成功`);
19832
20011
  return {
19833
20012
  downloadUrl: completeRes.DownloadURL,
19834
20013
  md5: fileMd5,
@@ -19848,85 +20027,233 @@ function generateTaskId(fileName, fileSize, fileMd5) {
19848
20027
  const input = `${fileName}-${fileSize}-${fileMd5}`;
19849
20028
  return __WEBPACK_EXTERNAL_MODULE_node_crypto_9ba42079__["default"].createHash("md5").update(input).digest("hex").slice(0, 32);
19850
20029
  }
20030
+ const MAX_METADATA_BOX_SIZE = 268435456;
20031
+ function readBoxHeader(fd, offset, limit) {
20032
+ if (offset + 8 > limit) return null;
20033
+ const head = Buffer.alloc(16);
20034
+ const read = __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__["default"].readSync(fd, head, 0, 16, offset);
20035
+ if (read < 8) return null;
20036
+ let size = head.readUInt32BE(0);
20037
+ const type = head.toString("latin1", 4, 8);
20038
+ let headerSize = 8;
20039
+ if (1 === size) {
20040
+ if (read < 16) return null;
20041
+ size = head.readUInt32BE(8) * 2 ** 32 + head.readUInt32BE(12);
20042
+ headerSize = 16;
20043
+ } else if (0 === size) size = limit - offset;
20044
+ if (size < headerSize || offset + size > limit) return null;
20045
+ return {
20046
+ type,
20047
+ size,
20048
+ headerSize
20049
+ };
20050
+ }
19851
20051
  function parseVideoMeta(filePath) {
19852
- let buf;
20052
+ let fd;
20053
+ let fileSize;
20054
+ try {
20055
+ fd = __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__["default"].openSync(filePath, "r");
20056
+ } catch {
20057
+ return null;
20058
+ }
19853
20059
  try {
19854
- buf = __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__["default"].readFileSync(filePath);
20060
+ fileSize = __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__["default"].fstatSync(fd).size;
20061
+ const state = {
20062
+ movie: null,
20063
+ tracks: [],
20064
+ trexDefaults: new Map(),
20065
+ fragmentDurations: new Map(),
20066
+ trafTrackId: 0,
20067
+ trafDefaultSampleDuration: 0
20068
+ };
20069
+ let offset = 0;
20070
+ while(offset + 8 <= fileSize){
20071
+ const header = readBoxHeader(fd, offset, fileSize);
20072
+ if (!header) break;
20073
+ if ("moov" === header.type || "moof" === header.type) {
20074
+ const bodyLength = header.size - header.headerSize;
20075
+ if (bodyLength > 0 && bodyLength <= MAX_METADATA_BOX_SIZE) {
20076
+ const body = Buffer.alloc(bodyLength);
20077
+ const read = __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__["default"].readSync(fd, body, 0, bodyLength, offset + header.headerSize);
20078
+ state.trafTrackId = 0;
20079
+ state.trafDefaultSampleDuration = 0;
20080
+ walkBoxes(body.subarray(0, read), 0, read, state);
20081
+ }
20082
+ }
20083
+ offset += header.size;
20084
+ }
20085
+ const { movie, tracks } = state;
20086
+ const video = tracks.find((t)=>"vide" === t.handler) || tracks.find((t)=>t.width > 0 && t.height > 0);
20087
+ if (!video) return null;
20088
+ let { width, height } = video;
20089
+ if (90 === video.rotation || 270 === video.rotation) [width, height] = [
20090
+ height,
20091
+ width
20092
+ ];
20093
+ return {
20094
+ width: Math.round(width),
20095
+ height: Math.round(height),
20096
+ duration: resolveDuration(movie, video, state.fragmentDurations),
20097
+ rotation: video.rotation,
20098
+ fileSize,
20099
+ codec: video.codec
20100
+ };
19855
20101
  } catch {
19856
20102
  return null;
20103
+ } finally{
20104
+ try {
20105
+ __WEBPACK_EXTERNAL_MODULE_node_fs_5ea92f0c__["default"].closeSync(fd);
20106
+ } catch {}
19857
20107
  }
19858
- let movie = null;
19859
- const tracks = [];
19860
- function walk(start, end) {
19861
- let off = start;
19862
- while(off + 8 <= end){
19863
- let size = buf.readUInt32BE(off);
19864
- const type = buf.toString("latin1", off + 4, off + 8);
19865
- let headerSize = 8;
19866
- if (1 === size) {
19867
- if (off + 16 > end) break;
19868
- const hi = buf.readUInt32BE(off + 8);
19869
- const lo = buf.readUInt32BE(off + 12);
19870
- size = hi * 2 ** 32 + lo;
19871
- headerSize = 16;
19872
- } else if (0 === size) size = end - off;
19873
- if (size < headerSize || off + size > end) break;
19874
- const bodyStart = off + headerSize;
19875
- const bodyEnd = off + size;
19876
- switch(type){
19877
- case "moov":
19878
- case "trak":
19879
- case "mdia":
19880
- case "minf":
19881
- case "stbl":
19882
- walk(bodyStart, bodyEnd);
20108
+ }
20109
+ function walkBoxes(buf, start, end, state) {
20110
+ let off = start;
20111
+ while(off + 8 <= end){
20112
+ let size = buf.readUInt32BE(off);
20113
+ const type = buf.toString("latin1", off + 4, off + 8);
20114
+ let headerSize = 8;
20115
+ if (1 === size) {
20116
+ if (off + 16 > end) break;
20117
+ const hi = buf.readUInt32BE(off + 8);
20118
+ const lo = buf.readUInt32BE(off + 12);
20119
+ size = hi * 2 ** 32 + lo;
20120
+ headerSize = 16;
20121
+ } else if (0 === size) size = end - off;
20122
+ if (size < headerSize || off + size > end) break;
20123
+ const bodyStart = off + headerSize;
20124
+ const bodyEnd = off + size;
20125
+ switch(type){
20126
+ case "trak":
20127
+ case "mdia":
20128
+ case "minf":
20129
+ case "stbl":
20130
+ case "mvex":
20131
+ case "traf":
20132
+ walkBoxes(buf, bodyStart, bodyEnd, state);
20133
+ break;
20134
+ case "mvhd":
20135
+ state.movie = parseMvhd(buf, bodyStart, bodyEnd);
20136
+ break;
20137
+ case "tkhd":
20138
+ state.tracks.push({
20139
+ ...parseTkhd(buf, bodyStart, bodyEnd),
20140
+ handler: null,
20141
+ codec: null,
20142
+ timescale: 0,
20143
+ mdhdDuration: 0,
20144
+ sttsDuration: 0
20145
+ });
20146
+ break;
20147
+ case "mdhd":
20148
+ {
20149
+ const mdhd = parseMvhd(buf, bodyStart, bodyEnd);
20150
+ if (mdhd && state.tracks.length) {
20151
+ const track = state.tracks[state.tracks.length - 1];
20152
+ track.timescale = mdhd.timescale;
20153
+ track.mdhdDuration = mdhd.duration;
20154
+ }
19883
20155
  break;
19884
- case "mvhd":
19885
- movie = parseMvhd(buf, bodyStart, bodyEnd);
20156
+ }
20157
+ case "hdlr":
20158
+ if (bodyEnd - bodyStart >= 12 && state.tracks.length) state.tracks[state.tracks.length - 1].handler = buf.toString("latin1", bodyStart + 8, bodyStart + 12);
20159
+ break;
20160
+ case "stsd":
20161
+ {
20162
+ const codec = parseStsdCodec(buf, bodyStart, bodyEnd);
20163
+ if (codec && state.tracks.length) state.tracks[state.tracks.length - 1].codec = codec;
19886
20164
  break;
19887
- case "tkhd":
19888
- tracks.push({
19889
- ...parseTkhd(buf, bodyStart, bodyEnd),
19890
- handler: null,
19891
- codec: null
19892
- });
20165
+ }
20166
+ case "stts":
20167
+ if (state.tracks.length) state.tracks[state.tracks.length - 1].sttsDuration = parseSttsDuration(buf, bodyStart, bodyEnd);
20168
+ break;
20169
+ case "trex":
20170
+ if (bodyStart + 20 > bodyEnd) break;
20171
+ state.trexDefaults.set(buf.readUInt32BE(bodyStart + 4), buf.readUInt32BE(bodyStart + 12));
20172
+ break;
20173
+ case "tfhd":
20174
+ {
20175
+ const tfhd = parseTfhd(buf, bodyStart, bodyEnd);
20176
+ state.trafTrackId = tfhd.trackId;
20177
+ state.trafDefaultSampleDuration = tfhd.defaultSampleDuration || state.trexDefaults.get(tfhd.trackId) || 0;
19893
20178
  break;
19894
- case "hdlr":
19895
- if (bodyEnd - bodyStart >= 12) {
19896
- const handler = buf.toString("latin1", bodyStart + 8, bodyStart + 12);
19897
- if (tracks.length) tracks[tracks.length - 1].handler = handler;
19898
- }
20179
+ }
20180
+ case "trun":
20181
+ {
20182
+ const duration = parseTrunDuration(buf, bodyStart, bodyEnd, state.trafDefaultSampleDuration);
20183
+ state.fragmentDurations.set(state.trafTrackId, (state.fragmentDurations.get(state.trafTrackId) || 0) + duration);
19899
20184
  break;
19900
- case "stsd":
19901
- {
19902
- const codec = parseStsdCodec(buf, bodyStart, bodyEnd);
19903
- if (codec && tracks.length) tracks[tracks.length - 1].codec = codec;
19904
- break;
19905
- }
19906
- }
19907
- off += size;
20185
+ }
19908
20186
  }
20187
+ off += size;
19909
20188
  }
19910
- walk(0, buf.length);
19911
- const video = tracks.find((t)=>"vide" === t.handler) || tracks.find((t)=>t.width > 0 && t.height > 0);
19912
- if (!movie || !video) return null;
19913
- const movieData = movie;
19914
- let { width, height } = video;
19915
- if (90 === video.rotation || 270 === video.rotation) [width, height] = [
19916
- height,
19917
- width
19918
- ];
20189
+ }
20190
+ const UNKNOWN_DURATION_32 = 0xffffffff;
20191
+ function isUsableDuration(duration) {
20192
+ return duration > 0 && duration !== UNKNOWN_DURATION_32;
20193
+ }
20194
+ function resolveDuration(movie, video, fragmentDurations) {
20195
+ if (movie && movie.timescale && isUsableDuration(movie.duration)) return movie.duration / movie.timescale;
20196
+ if (video.timescale) {
20197
+ if (isUsableDuration(video.mdhdDuration)) return video.mdhdDuration / video.timescale;
20198
+ if (video.sttsDuration > 0) return video.sttsDuration / video.timescale;
20199
+ const fragment = fragmentDurations.get(video.trackId) ?? (1 === fragmentDurations.size ? [
20200
+ ...fragmentDurations.values()
20201
+ ][0] : 0);
20202
+ if (fragment > 0) return fragment / video.timescale;
20203
+ }
20204
+ return 0;
20205
+ }
20206
+ function parseSttsDuration(buf, start, end) {
20207
+ if (start + 8 > end) return 0;
20208
+ const entryCount = buf.readUInt32BE(start + 4);
20209
+ let total = 0;
20210
+ for(let i = 0; i < entryCount; i++){
20211
+ const off = start + 8 + 8 * i;
20212
+ if (off + 8 > end) break;
20213
+ total += buf.readUInt32BE(off) * buf.readUInt32BE(off + 4);
20214
+ }
20215
+ return total;
20216
+ }
20217
+ function parseTfhd(buf, start, end) {
20218
+ if (start + 8 > end) return {
20219
+ trackId: 0,
20220
+ defaultSampleDuration: 0
20221
+ };
20222
+ const flags = buf.readUIntBE(start + 1, 3);
20223
+ const trackId = buf.readUInt32BE(start + 4);
20224
+ let off = start + 8;
20225
+ if (0x000001 & flags) off += 8;
20226
+ if (0x000002 & flags) off += 4;
20227
+ if (0x000008 & flags && off + 4 <= end) return {
20228
+ trackId,
20229
+ defaultSampleDuration: buf.readUInt32BE(off)
20230
+ };
19919
20231
  return {
19920
- width: Math.round(width),
19921
- height: Math.round(height),
19922
- duration: movieData.timescale ? movieData.duration / movieData.timescale : 0,
19923
- rotation: video.rotation,
19924
- fileSize: buf.length,
19925
- codec: video.codec
20232
+ trackId,
20233
+ defaultSampleDuration: 0
19926
20234
  };
19927
20235
  }
20236
+ function parseTrunDuration(buf, start, end, defaultSampleDuration) {
20237
+ if (start + 8 > end) return 0;
20238
+ const flags = buf.readUIntBE(start + 1, 3);
20239
+ const sampleCount = buf.readUInt32BE(start + 4);
20240
+ let off = start + 8;
20241
+ if (0x000001 & flags) off += 4;
20242
+ if (0x000004 & flags) off += 4;
20243
+ const hasDuration = (0x000100 & flags) !== 0;
20244
+ if (!hasDuration) return sampleCount * defaultSampleDuration;
20245
+ const entrySize = 4 + ((0x000200 & flags) !== 0 ? 4 : 0) + ((0x000400 & flags) !== 0 ? 4 : 0) + ((0x000800 & flags) !== 0 ? 4 : 0);
20246
+ let total = 0;
20247
+ for(let i = 0; i < sampleCount; i++){
20248
+ const entryOff = off + i * entrySize;
20249
+ if (entryOff + 4 > end) break;
20250
+ total += buf.readUInt32BE(entryOff);
20251
+ }
20252
+ return total;
20253
+ }
19928
20254
  function parseStsdCodec(buf, start, end) {
19929
20255
  if (start + 16 > end) return null;
20256
+ if (0 === buf.readUInt32BE(start + 4)) return null;
19930
20257
  return buf.toString("latin1", start + 12, start + 16).toLowerCase();
19931
20258
  }
19932
20259
  function parseMvhd(buf, start, end) {
@@ -19952,7 +20279,10 @@ function parseTkhd(buf, start, end) {
19952
20279
  const afterDuration = 1 === version ? 36 : 24;
19953
20280
  const matrixOff = start + afterDuration + 16;
19954
20281
  const whOff = matrixOff + 36;
20282
+ const trackIdOff = start + (1 === version ? 20 : 12);
20283
+ const trackId = trackIdOff + 4 <= end ? buf.readUInt32BE(trackIdOff) : 0;
19955
20284
  if (whOff + 8 > end) return {
20285
+ trackId,
19956
20286
  width: 0,
19957
20287
  height: 0,
19958
20288
  rotation: 0
@@ -19966,11 +20296,25 @@ function parseTkhd(buf, start, end) {
19966
20296
  else if (Math.abs(a + 1) < 0.01 && Math.abs(b) < 0.01) rotation = 180;
19967
20297
  else if (Math.abs(a) < 0.01 && Math.abs(b + 1) < 0.01) rotation = 270;
19968
20298
  return {
20299
+ trackId,
19969
20300
  width,
19970
20301
  height,
19971
20302
  rotation
19972
20303
  };
19973
20304
  }
20305
+ function parseVideoCodec(filePath) {
20306
+ return parseVideoMeta(filePath)?.codec ?? null;
20307
+ }
20308
+ function buildVideoMetaFromParams(filePath, metadata) {
20309
+ return {
20310
+ width: Math.round(metadata.width),
20311
+ height: Math.round(metadata.height),
20312
+ duration: metadata.duration,
20313
+ rotation: 0,
20314
+ fileSize: metadata.fileSize,
20315
+ codec: parseVideoCodec(filePath)
20316
+ };
20317
+ }
19974
20318
  const MAX_DURATION_SECONDS = 28800;
19975
20319
  const MAX_FILE_SIZE = 21474836480;
19976
20320
  const ALLOWED_EXTENSIONS = [
@@ -19981,6 +20325,7 @@ const ALLOWED_CODECS = [
19981
20325
  "avc3"
19982
20326
  ];
19983
20327
  const MIN_TITLE_LENGTH = 6;
20328
+ const MAX_TITLE_LENGTH = 16;
19984
20329
  function formatFileSize(bytes) {
19985
20330
  if (bytes < 1048576) return `${(bytes / 1024).toFixed(2)} KB`;
19986
20331
  if (bytes < 1073741824) return `${(bytes / 1024 / 1024).toFixed(2)} MB`;
@@ -20007,26 +20352,62 @@ function validateShipinhaoVideo(filePath, meta) {
20007
20352
  function validateShipinhaoTitle(title) {
20008
20353
  if (void 0 === title) return null;
20009
20354
  const trimmed = title.trim();
20355
+ if ("" === trimmed) return null;
20010
20356
  const length = [
20011
20357
  ...trimmed
20012
20358
  ].length;
20013
- if (length < MIN_TITLE_LENGTH) return `视频号标题至少需要 ${MIN_TITLE_LENGTH} 个字符,请补充后重试。`;
20359
+ if (length < MIN_TITLE_LENGTH || length > MAX_TITLE_LENGTH) return `视频号标题需要在 ${MIN_TITLE_LENGTH}-${MAX_TITLE_LENGTH} 个字符之间,当前 ${length} 个字符,请调整后重试。`;
20014
20360
  return null;
20015
20361
  }
20016
- async function getTraceKey(auth, http, logger) {
20017
- logger.info("[getTraceKey] 开始获取 traceKey...");
20362
+ const POST_CREATE_PAGE_URL = "https://channels.weixin.qq.com/micro/content/post/create";
20363
+ const MICRO_CONTENT_BASE = "https://channels.weixin.qq.com/micro/content/cgi-bin/mmfinderassistant-bin";
20364
+ function resolveClientContext(params, fallbackUin) {
20365
+ const extra = params.extraParam || {};
20366
+ const deviceIdCookie = params.cookies.find((c)=>"device_id" === c.name || "finger_print_device_id" === c.name)?.value;
20367
+ return {
20368
+ aId: "string" == typeof extra.aId ? extra.aId : "",
20369
+ fingerPrintDeviceId: "string" == typeof extra.fingerPrintDeviceId ? extra.fingerPrintDeviceId : deviceIdCookie || "",
20370
+ uin: "string" == typeof extra.uin ? extra.uin : String(fallbackUin)
20371
+ };
20372
+ }
20373
+ function buildPublishHeaders(cookieString, client) {
20374
+ const headers = {
20375
+ cookie: cookieString,
20376
+ referer: POST_CREATE_PAGE_URL,
20377
+ origin: "https://channels.weixin.qq.com",
20378
+ "content-type": "application/json"
20379
+ };
20380
+ if (client.fingerPrintDeviceId) headers["finger-print-device-id"] = client.fingerPrintDeviceId;
20381
+ if (client.uin) headers["x-wechat-uin"] = client.uin;
20382
+ return headers;
20383
+ }
20384
+ function buildPublishQuery(client) {
20385
+ const query = {
20386
+ _rid: rid(),
20387
+ _pageUrl: POST_CREATE_PAGE_URL
20388
+ };
20389
+ if (client.aId) query._aid = client.aId;
20390
+ return query;
20391
+ }
20392
+ function buildCommonBody(finderUsername) {
20393
+ return {
20394
+ timestamp: String(Date.now()),
20395
+ _log_finder_uin: "",
20396
+ _log_finder_id: finderUsername,
20397
+ rawKeyBuff: "",
20398
+ pluginSessionId: null,
20399
+ scene: 7,
20400
+ reqScene: 7
20401
+ };
20402
+ }
20403
+ async function getTraceKey(auth, client, http, logger) {
20018
20404
  const res = await http.api({
20019
20405
  method: "POST",
20020
- url: "https://channels.weixin.qq.com/micro/content/cgi-bin/mmfinderassistant-bin/post/get-finder-post-trace-key",
20406
+ url: `${MICRO_CONTENT_BASE}/post/get-finder-post-trace-key`,
20407
+ params: buildPublishQuery(client),
20021
20408
  data: {
20022
20409
  objectId: "",
20023
- timestamp: String(Date.now()),
20024
- _log_finder_uin: "",
20025
- _log_finder_id: auth.finderUsername,
20026
- rawKeyBuff: "",
20027
- pluginSessionId: null,
20028
- scene: 7,
20029
- reqScene: 7
20410
+ ...buildCommonBody(auth.finderUsername)
20030
20411
  },
20031
20412
  defaultErrorMsg: "获取 traceKey 失败"
20032
20413
  });
@@ -20034,18 +20415,38 @@ async function getTraceKey(auth, http, logger) {
20034
20415
  logger.error("[getTraceKey] 获取失败:", JSON.stringify(res));
20035
20416
  throw new Error(`获取 traceKey 失败: ${JSON.stringify(res)}`);
20036
20417
  }
20037
- logger.info(`[getTraceKey] 获取成功: ${res.data.traceKey}`);
20038
20418
  return res.data.traceKey;
20039
20419
  }
20420
+ async function getObjectTagKey(auth, client, http, logger) {
20421
+ try {
20422
+ const res = await http.api({
20423
+ method: "POST",
20424
+ url: `${MICRO_CONTENT_BASE}/post/finder_get_object_tag_list`,
20425
+ params: buildPublishQuery(client),
20426
+ data: {
20427
+ source: 1,
20428
+ ...buildCommonBody(auth.finderUsername)
20429
+ },
20430
+ defaultErrorMsg: "获取内容声明标注失败"
20431
+ });
20432
+ if (0 !== res.errCode || !res.data?.tagKey) {
20433
+ logger.warn(`[getObjectTagKey] 未取到 tagKey: ${JSON.stringify(res)}`);
20434
+ return null;
20435
+ }
20436
+ return res.data.tagKey;
20437
+ } catch (error) {
20438
+ logger.warn(`[getObjectTagKey] 获取 tagKey 异常: ${stringifyError(error)}`);
20439
+ return null;
20440
+ }
20441
+ }
20040
20442
  async function submitAndPollTranscode(opts) {
20041
- const { videoUrl, videoMeta, traceKey, uploadStartTime, uploadEndTime, finderUsername, http, logger } = opts;
20042
- logger.info("[submitAndPollTranscode] 开始提交转码任务...");
20443
+ const { videoUrl, videoMeta, traceKey, uploadStartTime, uploadEndTime, finderUsername, client, http, logger } = opts;
20043
20444
  const finderUrl = videoUrl.includes("wxapp.tc.qq.com") ? `https://finder.video.qq.com${videoUrl.split("qq.com")[1]}` : videoUrl;
20044
- logger.info(`[submitAndPollTranscode] 视频URL: ${finderUrl}`);
20045
20445
  logger.info(`[submitAndPollTranscode] 视频尺寸: ${videoMeta.width}x${videoMeta.height}, 时长: ${videoMeta.duration}s`);
20046
20446
  const submitRes = await http.api({
20047
20447
  method: "POST",
20048
- url: "https://channels.weixin.qq.com/micro/content/cgi-bin/mmfinderassistant-bin/post/post_clip_video",
20448
+ url: `${MICRO_CONTENT_BASE}/post/post_clip_video`,
20449
+ params: buildPublishQuery(client),
20049
20450
  data: {
20050
20451
  url: finderUrl,
20051
20452
  timeStart: 0,
@@ -20069,13 +20470,7 @@ async function submitAndPollTranscode(opts) {
20069
20470
  targetHeight: videoMeta.height,
20070
20471
  type: 4,
20071
20472
  useAstraThumbCover: 1,
20072
- timestamp: String(Date.now()),
20073
- _log_finder_uin: "",
20074
- _log_finder_id: finderUsername,
20075
- rawKeyBuff: "",
20076
- pluginSessionId: null,
20077
- scene: 7,
20078
- reqScene: 7
20473
+ ...buildCommonBody(finderUsername)
20079
20474
  },
20080
20475
  defaultErrorMsg: "提交转码失败"
20081
20476
  });
@@ -20084,43 +20479,39 @@ async function submitAndPollTranscode(opts) {
20084
20479
  throw new Error(`提交转码失败: ${JSON.stringify(submitRes)}`);
20085
20480
  }
20086
20481
  const { clipKey, draftId } = submitRes.data;
20087
- logger.info(`[submitAndPollTranscode] 转码任务已提交,clipKey: ${clipKey}, draftId: ${draftId}`);
20088
- const maxPolls = 60;
20089
20482
  const pollInterval = 5000;
20483
+ const pollBudget = Math.min(1800000, 300000 + 1000 * Math.ceil(1.5 * videoMeta.duration));
20484
+ const maxPolls = Math.ceil(pollBudget / pollInterval);
20090
20485
  let pollCount = 0;
20091
- logger.info(`[submitAndPollTranscode] 开始轮询转码结果,最多 ${maxPolls} 次,间隔 ${pollInterval / 1000}s`);
20092
20486
  while(pollCount < maxPolls){
20093
20487
  await sleep(pollInterval);
20094
20488
  pollCount++;
20095
20489
  const pollRes = await http.api({
20096
20490
  method: "POST",
20097
- url: "https://channels.weixin.qq.com/micro/content/cgi-bin/mmfinderassistant-bin/post/post_clip_video_result",
20491
+ url: `${MICRO_CONTENT_BASE}/post/post_clip_video_result`,
20492
+ params: buildPublishQuery(client),
20098
20493
  data: {
20099
20494
  clipKey,
20100
20495
  draftId,
20101
- timestamp: String(Date.now()),
20102
- _log_finder_uin: "",
20103
- _log_finder_id: finderUsername,
20104
- rawKeyBuff: "",
20105
- pluginSessionId: null,
20106
- scene: 7,
20107
- reqScene: 7
20496
+ ...buildCommonBody(finderUsername)
20108
20497
  },
20109
20498
  defaultErrorMsg: "转码轮询失败"
20499
+ }, {
20500
+ timeout: 60000,
20501
+ retries: 2,
20502
+ retryDelay: 3000
20110
20503
  });
20111
20504
  if (0 !== pollRes.errCode) {
20112
20505
  logger.error(`[submitAndPollTranscode] 转码轮询失败 (poll ${pollCount}):`, JSON.stringify(pollRes));
20113
20506
  throw new Error(`转码轮询失败 (poll ${pollCount}): ${JSON.stringify(pollRes)}`);
20114
20507
  }
20115
20508
  const { flag, url, width, height, duration, md5, fileSize } = pollRes.data || {};
20116
- logger.info(`[submitAndPollTranscode] 轮询第 ${pollCount} 次,flag=${flag}`);
20117
20509
  if (1 === flag) {
20118
20510
  if (!url || !width || !height || !duration || !md5 || !fileSize) {
20119
20511
  logger.error("[submitAndPollTranscode] 转码完成但返回数据不完整:", JSON.stringify(pollRes.data));
20120
20512
  throw new Error(`转码完成但返回数据不完整: ${JSON.stringify(pollRes.data)}`);
20121
20513
  }
20122
20514
  logger.info(`[submitAndPollTranscode] 转码完成! 用时: ${pollCount * pollInterval / 1000}s`);
20123
- logger.info(`[submitAndPollTranscode] 视频信息: ${width}x${height}, 时长: ${duration}s, 大小: ${fileSize}`);
20124
20515
  return {
20125
20516
  clipKey,
20126
20517
  url,
@@ -20131,7 +20522,7 @@ async function submitAndPollTranscode(opts) {
20131
20522
  fileSize
20132
20523
  };
20133
20524
  }
20134
- if (2 === flag) logger.info(`[submitAndPollTranscode] 转码中... (${pollCount}/${maxPolls})`);
20525
+ if (2 === flag) ;
20135
20526
  else {
20136
20527
  logger.error(`[submitAndPollTranscode] 转码失败,未知 flag=${flag}:`, JSON.stringify(pollRes.data));
20137
20528
  throw new Error(`转码失败,未知 flag=${flag}: ${JSON.stringify(pollRes.data)}`);
@@ -20141,162 +20532,151 @@ async function submitAndPollTranscode(opts) {
20141
20532
  throw new Error(`转码超时 (${maxPolls * pollInterval / 1000}s)`);
20142
20533
  }
20143
20534
  async function publishVideo(opts) {
20144
- const { params, auth, clipResult, videoUpload, coverUpload, traceKey, uploadStartTime, uploadEndTime, proxyHttp, logger } = opts;
20145
- logger.info("[publishVideo] 开始构建发布请求...");
20146
- const coverFinderUrl = coverUpload.downloadUrl.includes("wxapp.tc.qq.com") ? `https://finder.video.qq.com${coverUpload.downloadUrl.split("qq.com")[1]}` : coverUpload.downloadUrl;
20147
- logger.info(`[publishVideo] 封面URL: ${coverFinderUrl}`);
20535
+ const { params, auth, client, clipResult, videoUpload, coverUpload, verticalCoverUpload, videoMeta, traceKey, tagKey, uploadStartTime, uploadEndTime, proxyHttp, logger } = opts;
20536
+ const toFinderUrl = (url)=>url.includes("wxapp.tc.qq.com") ? `https://finder.video.qq.com${url.split("qq.com")[1]}` : url;
20537
+ const thumbFinderUrl = toFinderUrl(coverUpload.downloadUrl);
20538
+ const coverFinderUrl = toFinderUrl(verticalCoverUpload.downloadUrl);
20148
20539
  const md5sumUuid = __WEBPACK_EXTERNAL_MODULE_node_crypto_9ba42079__["default"].randomUUID();
20149
- logger.info("[publishVideo] 发布参数:");
20150
- logger.info(` - 标题: ${params.title || "(无)"}`);
20151
- logger.info(` - 描述: ${params.description?.substring(0, 50)}${params.description?.length > 50 ? "..." : ""}`);
20152
- logger.info(` - 定时发布: ${params.scheduleTime ? new Date(1000 * params.scheduleTime).toLocaleString() : "立即发布"}`);
20153
- logger.info(` - 地理位置: ${params.location ? `${params.location.city} (${params.location.latitude},${params.location.longitude})` : "(无)"}`);
20154
- const publishRes = await proxyHttp.api({
20155
- method: "POST",
20156
- url: "https://channels.weixin.qq.com/micro/content/cgi-bin/mmfinderassistant-bin/post/post_create",
20157
- data: {
20158
- objectType: 0,
20159
- longitude: params.location?.longitude || 0,
20160
- latitude: params.location?.latitude || 0,
20161
- feedLongitude: 0,
20162
- feedLatitude: 0,
20163
- originalFlag: 0,
20164
- topics: [],
20165
- isFullPost: 1,
20166
- handleFlag: 2,
20167
- videoClipTaskId: clipResult.clipKey,
20168
- traceInfo: {
20169
- traceKey,
20170
- uploadCdnStart: uploadStartTime,
20171
- uploadCdnEnd: uploadEndTime
20172
- },
20173
- objectDesc: {
20174
- mpTitle: "",
20175
- description: params.description,
20176
- shortTitle: params.title || "",
20177
- extReading: {},
20540
+ const description = params.description;
20541
+ const objectDesc = {
20542
+ mpTitle: "",
20543
+ description,
20544
+ extReading: buildExtReading(params.link),
20545
+ mediaType: 4,
20546
+ location: payload_buildLocation(params.location),
20547
+ topic: buildTopic({
20548
+ description: params.description,
20549
+ topics: params.topics,
20550
+ mentionedUsers: params.mentionedUsers,
20551
+ collection: params.collection
20552
+ }),
20553
+ event: buildEvent(params.event),
20554
+ mentionedUser: buildMentionedUser(params.mentionedUsers),
20555
+ media: [
20556
+ {
20557
+ url: clipResult.url,
20558
+ fileSize: clipResult.fileSize,
20559
+ thumbUrl: thumbFinderUrl,
20560
+ fullThumbUrl: thumbFinderUrl,
20561
+ coverUrl: coverFinderUrl,
20562
+ fullCoverUrl: coverFinderUrl,
20563
+ shareCoverUrl: coverFinderUrl,
20178
20564
  mediaType: 4,
20179
- location: params.location ? {
20180
- latitude: params.location.latitude,
20181
- longitude: params.location.longitude,
20182
- city: params.location.city,
20183
- poiClassifyId: params.location.poiClassifyId || ""
20184
- } : {
20185
- latitude: 0,
20186
- longitude: 0,
20187
- city: "",
20188
- poiClassifyId: ""
20189
- },
20190
- topic: {
20191
- finderTopicInfo: `<finder><version>1</version><valuecount>1</valuecount><style><at></at></style><value0><![CDATA[${params.description}]]></value0></finder>`
20192
- },
20193
- event: {},
20194
- mentionedUser: [],
20195
- media: [
20196
- {
20197
- url: clipResult.url,
20198
- fileSize: clipResult.fileSize,
20199
- thumbUrl: coverFinderUrl,
20200
- fullThumbUrl: coverFinderUrl,
20201
- coverUrl: coverFinderUrl,
20202
- fullCoverUrl: coverFinderUrl,
20203
- shareCoverUrl: coverFinderUrl,
20204
- mediaType: 4,
20205
- videoPlayLen: Math.round(clipResult.duration),
20206
- width: clipResult.width,
20207
- height: clipResult.height,
20208
- md5sum: md5sumUuid,
20209
- cardShowStyle: 2,
20210
- urlCdnTaskId: clipResult.clipKey
20211
- }
20212
- ],
20213
- member: {}
20214
- },
20215
- effectiveTime: params.scheduleTime || 0,
20216
- report: {
20217
- clipKey: clipResult.clipKey,
20218
- draftId: clipResult.clipKey,
20219
- timestamp: String(Date.now()),
20220
- _log_finder_uin: "",
20221
- _log_finder_id: auth.finderUsername,
20222
- rawKeyBuff: "",
20223
- pluginSessionId: null,
20224
- scene: 7,
20225
- reqScene: 7,
20226
- height: clipResult.height,
20565
+ videoPlayLen: Math.round(clipResult.duration),
20227
20566
  width: clipResult.width,
20228
- duration: clipResult.duration,
20229
- fileSize: videoUpload.fileSize,
20230
- uploadCost: (uploadEndTime - uploadStartTime) * 1000
20231
- },
20232
- postFlag: 0,
20233
- mode: 1,
20234
- clientid: __WEBPACK_EXTERNAL_MODULE_node_crypto_9ba42079__["default"].randomUUID(),
20235
- timestamp: String(Date.now()),
20236
- _log_finder_uin: "",
20237
- _log_finder_id: auth.finderUsername,
20238
- rawKeyBuff: "",
20239
- pluginSessionId: null,
20240
- scene: 7,
20241
- reqScene: 7
20567
+ height: clipResult.height,
20568
+ md5sum: md5sumUuid,
20569
+ cardShowStyle: 2,
20570
+ urlCdnTaskId: clipResult.clipKey
20571
+ }
20572
+ ],
20573
+ shortTitle: params.title ? [
20574
+ {
20575
+ shortTitle: params.title
20576
+ }
20577
+ ] : [],
20578
+ member: {}
20579
+ };
20580
+ const publishData = {
20581
+ objectType: 0,
20582
+ longitude: 0,
20583
+ latitude: 0,
20584
+ feedLongitude: 0,
20585
+ feedLatitude: 0,
20586
+ originalFlag: params.originalFlag ?? 0,
20587
+ topics: params.topics || [],
20588
+ isFullPost: 1,
20589
+ handleFlag: 2,
20590
+ videoClipTaskId: clipResult.clipKey,
20591
+ traceInfo: {
20592
+ traceKey,
20593
+ uploadCdnStart: uploadStartTime,
20594
+ uploadCdnEnd: uploadEndTime
20242
20595
  },
20596
+ objectDesc,
20597
+ report: {
20598
+ clipKey: clipResult.clipKey,
20599
+ draftId: clipResult.clipKey,
20600
+ ...buildCommonBody(auth.finderUsername),
20601
+ height: videoMeta.height,
20602
+ width: videoMeta.width,
20603
+ duration: videoMeta.duration,
20604
+ fileSize: videoUpload.fileSize,
20605
+ uploadCost: (uploadEndTime - uploadStartTime) * 1000
20606
+ },
20607
+ postFlag: 0,
20608
+ mode: 1,
20609
+ clientid: __WEBPACK_EXTERNAL_MODULE_node_crypto_9ba42079__["default"].randomUUID(),
20610
+ ...buildCommonBody(auth.finderUsername)
20611
+ };
20612
+ if (params.scheduledTime) publishData.effectiveTime = params.scheduledTime;
20613
+ if (params.tagInfo && tagKey) publishData.tagInfo = buildTagInfo(params.tagInfo, tagKey);
20614
+ const publishRes = await proxyHttp.api({
20615
+ method: "POST",
20616
+ url: `${MICRO_CONTENT_BASE}/post/post_create`,
20617
+ params: buildPublishQuery(client),
20618
+ data: publishData,
20243
20619
  defaultErrorMsg: "发布视频失败"
20244
20620
  });
20245
- logger.info(`[publishVideo] 发布响应: errCode=${publishRes.errCode}, baseResp.errcode=${publishRes.data?.baseResp?.errcode}`);
20621
+ logger.info(`[publishVideo] 发布结果: errCode=${publishRes.errCode}, baseResp.errcode=${publishRes.data?.baseResp?.errcode}`);
20246
20622
  return publishRes;
20247
20623
  }
20248
20624
  function sleep(ms) {
20249
20625
  return new Promise((resolve)=>setTimeout(resolve, ms));
20250
20626
  }
20627
+ async function resolveLocalCoverPath(coverPath, label, tmpCachePath, logger) {
20628
+ if (!/^https?:\/\//i.test(coverPath)) return coverPath;
20629
+ const fileName = (0, __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.getFilenameFromUrl)(coverPath);
20630
+ const savePath = __WEBPACK_EXTERNAL_MODULE_node_path_c5b9b54f__["default"].join(tmpCachePath, `${Date.now()}-${label}-${fileName}`);
20631
+ await (0, __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.downloadImage)(coverPath, savePath);
20632
+ return savePath;
20633
+ }
20251
20634
  const shipinhaoPublishVideo_mock_mockAction = async (task, params)=>{
20252
- task.logger.info("[shipinhaoPublishVideo] 开始执行视频号视频发布 - Mock API 方式");
20253
20635
  const updateTaskState = task.taskStageStore?.update?.bind(task.taskStageStore, task.taskId || "");
20254
20636
  let currentStep = "初始化";
20255
20637
  try {
20256
20638
  currentStep = "解析认证信息";
20257
20639
  const cookieString = params.cookies.map((c)=>`${c.name}=${c.value}`).join("; ");
20258
- const headers = {
20259
- cookie: cookieString
20260
- };
20640
+ const http = new Http({
20641
+ headers: {
20642
+ cookie: cookieString
20643
+ }
20644
+ });
20645
+ currentStep = "验证发布参数";
20646
+ if (!params.videoPath) return (0, __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.response)(414, "视频文件路径不能为空", "");
20647
+ if (!params.coverPath) return (0, __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.response)(414, "横屏封面图片路径不能为空", "");
20648
+ currentStep = "获取上传认证";
20649
+ const auth = await getShipinhaoUploadAuth(cookieString, http);
20650
+ const client = resolveClientContext(params, auth.uin);
20651
+ const publishHeaders = buildPublishHeaders(cookieString, client);
20652
+ const microHttp = new Http({
20653
+ headers: publishHeaders
20654
+ });
20261
20655
  const args = [
20262
20656
  {
20263
- headers
20657
+ headers: publishHeaders
20264
20658
  },
20265
20659
  task.logger,
20266
20660
  params.proxyLoc,
20267
20661
  params.accountId,
20268
20662
  "shipinhao"
20269
20663
  ];
20270
- const http = new Http({
20271
- headers
20272
- });
20273
20664
  const proxyHttp = new Http(...args);
20274
- currentStep = "验证发布参数";
20275
- if (!params.videoPath) return (0, __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.response)(414, "视频文件路径不能为空", "");
20276
- if (!params.coverPath) return (0, __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.response)(414, "封面图片路径不能为空", "");
20277
- currentStep = "获取上传认证";
20278
- task.logger.info("[shipinhaoPublishVideo] 获取上传认证...");
20279
- const auth = await getShipinhaoUploadAuth(cookieString, http);
20280
- currentStep = "解析视频元数据";
20281
- task.logger.info("[shipinhaoPublishVideo] 解析视频元数据...");
20282
- const videoMeta = parseVideoMeta(params.videoPath);
20283
- if (!videoMeta) return (0, __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.response)(414, "视频文件解析失败,请检查文件格式(仅支持 MP4)", "");
20665
+ currentStep = "组装视频元数据";
20666
+ const videoMeta = buildVideoMetaFromParams(params.videoPath, params.videoMetadata);
20284
20667
  task.logger.info(`[shipinhaoPublishVideo] 视频: ${videoMeta.width}×${videoMeta.height}, ${videoMeta.duration.toFixed(2)}s, ${(videoMeta.fileSize / 1024 / 1024).toFixed(2)}MB, codec=${videoMeta.codec || "unknown"}`);
20285
20668
  currentStep = "校验视频限制";
20286
20669
  const validationError = validateShipinhaoVideo(params.videoPath, videoMeta);
20287
20670
  if (validationError) {
20288
- task.logger.error(`[shipinhaoPublishVideo] 视频校验未通过: ${validationError}`);
20289
20671
  await updateTaskState?.({
20290
20672
  state: __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.TaskState.FAILED,
20291
20673
  error: validationError
20292
20674
  });
20293
20675
  return (0, __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.response)(414, validationError, "");
20294
20676
  }
20295
- task.logger.info("[shipinhaoPublishVideo] 视频校验通过");
20296
20677
  currentStep = "校验标题";
20297
20678
  const titleError = validateShipinhaoTitle(params.title);
20298
20679
  if (titleError) {
20299
- task.logger.error(`[shipinhaoPublishVideo] 标题校验未通过: ${titleError}`);
20300
20680
  await updateTaskState?.({
20301
20681
  state: __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.TaskState.FAILED,
20302
20682
  error: titleError
@@ -20304,14 +20684,14 @@ const shipinhaoPublishVideo_mock_mockAction = async (task, params)=>{
20304
20684
  return (0, __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.response)(414, titleError, "");
20305
20685
  }
20306
20686
  currentStep = "获取 traceKey";
20307
- task.logger.info("[shipinhaoPublishVideo] 获取 traceKey...");
20308
- const traceKey = await getTraceKey(auth, http, task.logger);
20687
+ const traceKey = await getTraceKey(auth, client, microHttp, task.logger);
20688
+ let tagKey = null;
20689
+ if (params.tagInfo) {
20690
+ currentStep = "获取内容声明 tagKey";
20691
+ tagKey = await getObjectTagKey(auth, client, microHttp, task.logger);
20692
+ }
20309
20693
  const uploadStartTime = Math.floor(Date.now() / 1000);
20310
- task.logger.info(`[shipinhaoPublishVideo] 上传开始时间: ${uploadStartTime}`);
20311
20694
  currentStep = "上传视频";
20312
- task.logger.info("[shipinhaoPublishVideo] 上传视频...");
20313
- task.logger.info(`[shipinhaoPublishVideo] 视频路径: ${params.videoPath}`);
20314
- task.logger.info(`[shipinhaoPublishVideo] 视频文件类型: ${auth.videoFileType}`);
20315
20695
  const videoUpload = await uploader_uploadFile({
20316
20696
  filePath: params.videoPath,
20317
20697
  fileType: auth.videoFileType,
@@ -20320,24 +20700,32 @@ const shipinhaoPublishVideo_mock_mockAction = async (task, params)=>{
20320
20700
  http,
20321
20701
  logger: task.logger
20322
20702
  });
20323
- task.logger.info(`[shipinhaoPublishVideo] 视频上传完成,URL: ${videoUpload.downloadUrl}`);
20324
20703
  const uploadEndTime = Math.floor(Date.now() / 1000);
20325
- task.logger.info(`[shipinhaoPublishVideo] 上传结束时间: ${uploadEndTime}, 耗时: ${uploadEndTime - uploadStartTime}s`);
20326
- currentStep = "上传封面";
20327
- task.logger.info("[shipinhaoPublishVideo] 上传封面...");
20328
- task.logger.info(`[shipinhaoPublishVideo] 封面路径: ${params.coverPath}`);
20329
- task.logger.info(`[shipinhaoPublishVideo] 封面文件类型: ${auth.pictureFileType}`);
20704
+ task.logger.info(`[shipinhaoPublishVideo] 耗时: ${uploadEndTime - uploadStartTime}s`);
20705
+ currentStep = "上传横屏封面";
20706
+ const localCoverPath = await resolveLocalCoverPath(params.coverPath, "横屏封面", task.getTmpPath(), task.logger);
20330
20707
  const coverUpload = await uploader_uploadFile({
20331
- filePath: params.coverPath,
20708
+ filePath: localCoverPath,
20332
20709
  fileType: auth.pictureFileType,
20333
20710
  uin: auth.uin,
20334
20711
  authKey: auth.authKey,
20335
20712
  http,
20336
20713
  logger: task.logger
20337
20714
  });
20338
- task.logger.info(`[shipinhaoPublishVideo] 封面上传完成,URL: ${coverUpload.downloadUrl}`);
20715
+ let verticalCoverUpload = coverUpload;
20716
+ if (params.verticalCoverPath) {
20717
+ currentStep = "上传竖屏封面";
20718
+ const localVerticalPath = await resolveLocalCoverPath(params.verticalCoverPath, "竖屏封面", task.getTmpPath(), task.logger);
20719
+ verticalCoverUpload = await uploader_uploadFile({
20720
+ filePath: localVerticalPath,
20721
+ fileType: auth.pictureFileType,
20722
+ uin: auth.uin,
20723
+ authKey: auth.authKey,
20724
+ http,
20725
+ logger: task.logger
20726
+ });
20727
+ }
20339
20728
  currentStep = "提交转码";
20340
- task.logger.info("[shipinhaoPublishVideo] 提交转码...");
20341
20729
  const clipResult = await submitAndPollTranscode({
20342
20730
  videoUrl: videoUpload.downloadUrl,
20343
20731
  videoMeta,
@@ -20345,21 +20733,24 @@ const shipinhaoPublishVideo_mock_mockAction = async (task, params)=>{
20345
20733
  uploadStartTime,
20346
20734
  uploadEndTime,
20347
20735
  finderUsername: auth.finderUsername,
20348
- http,
20736
+ client,
20737
+ http: microHttp,
20349
20738
  logger: task.logger
20350
20739
  });
20351
20740
  currentStep = "发布视频";
20352
- task.logger.info("[shipinhaoPublishVideo] 发布视频...");
20353
- task.logger.info(`[shipinhaoPublishVideo] clipKey: ${clipResult.clipKey}`);
20354
20741
  let publishResult;
20355
20742
  try {
20356
20743
  publishResult = await publishVideo({
20357
20744
  params,
20358
20745
  auth,
20746
+ client,
20359
20747
  clipResult,
20360
20748
  videoUpload,
20361
20749
  coverUpload,
20750
+ verticalCoverUpload,
20751
+ videoMeta,
20362
20752
  traceKey,
20753
+ tagKey,
20363
20754
  uploadStartTime,
20364
20755
  uploadEndTime,
20365
20756
  proxyHttp,
@@ -20371,7 +20762,6 @@ const shipinhaoPublishVideo_mock_mockAction = async (task, params)=>{
20371
20762
  const classified = classifyPublishError(handledError);
20372
20763
  if (classified) {
20373
20764
  const message = `${classified.userMessage}${task.debug ? ` ${http.proxyInfo}` : ""}`;
20374
- task.logger.error(`[shipinhaoPublishVideo] ${classified.category},直接返回: ${handledError.message} (code=${handledError.code})`, stringifyError(handledError));
20375
20765
  await updateTaskState?.({
20376
20766
  state: __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.TaskState.FAILED,
20377
20767
  error: message
@@ -20386,12 +20776,10 @@ const shipinhaoPublishVideo_mock_mockAction = async (task, params)=>{
20386
20776
  }
20387
20777
  task.logger.info(`[shipinhaoPublishVideo] publishResult: ${JSON.stringify(publishResult)}`);
20388
20778
  const resultCode = publishResult.data?.baseResp?.errcode ?? publishResult.errCode;
20389
- const resultMsg = publishResult.data?.baseResp?.errmsg ?? "发布成功";
20779
+ const resultMsg = publishResult.data?.baseResp?.errmsg ?? (0 === resultCode ? "发布成功" : `发布失败(errCode=${resultCode})`);
20390
20780
  if (0 === resultCode) {
20391
- task.logger.info("[shipinhaoPublishVideo] 发布成功");
20392
- task.logger.info(`[shipinhaoPublishVideo] 作品ID: ${clipResult.clipKey}`);
20393
- task.logger.info(`[shipinhaoPublishVideo] 视频URL: ${clipResult.url}`);
20394
- task.logger.info(`[shipinhaoPublishVideo] 封面URL: ${coverUpload.downloadUrl}`);
20781
+ const publishId = extractEncFileKey(verticalCoverUpload.downloadUrl);
20782
+ if (!publishId) task.logger.error(`[shipinhaoPublishVideo] 封面 DownloadURL 中未解析到 encfilekey,关联 id 为空: ${verticalCoverUpload.downloadUrl}`);
20395
20783
  await updateTaskState?.({
20396
20784
  state: __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.TaskState.SUCCESS,
20397
20785
  result: {
@@ -20407,41 +20795,49 @@ const shipinhaoPublishVideo_mock_mockAction = async (task, params)=>{
20407
20795
  uid: params.uid,
20408
20796
  publishParams: {
20409
20797
  videoPath: params.videoPath,
20410
- coverPath: params.coverPath
20798
+ coverPath: params.coverPath,
20799
+ verticalCoverPath: params.verticalCoverPath,
20800
+ title: params.title,
20801
+ topics: params.topics,
20802
+ mentionedUsers: params.mentionedUsers,
20803
+ collection: params.collection,
20804
+ event: params.event,
20805
+ link: params.link,
20806
+ tagInfo: params.tagInfo,
20807
+ originalFlag: params.originalFlag,
20808
+ scheduledTime: params.scheduledTime
20411
20809
  },
20412
20810
  platform: "shipinhao"
20413
20811
  });
20414
- task.logger.info("[shipinhaoPublishVideo] 日志上报完成");
20415
- return (0, __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.response)(0, "发布成功", clipResult.clipKey);
20812
+ return (0, __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.response)(0, "发布成功", publishId);
20416
20813
  }
20417
20814
  let errorMessage = resultMsg;
20418
20815
  if (-11224 === resultCode) errorMessage = "视频号管理员完成实名且绑定手机号后才可以发表";
20419
20816
  else if (300333 === resultCode || 300334 === resultCode) errorMessage = "登录失效";
20420
20817
  else if (300330 === resultCode) errorMessage = "未登录";
20818
+ else if (300002 === resultCode) errorMessage = "官方平台在校验音乐/位置/定时信息时失败了,请重新编辑后发布";
20421
20819
  task.logger.error(`[shipinhaoPublishVideo] 发布失败: ${errorMessage} (errCode=${resultCode})`);
20422
20820
  await updateTaskState?.({
20423
20821
  state: __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.TaskState.FAILED,
20424
20822
  error: errorMessage
20425
20823
  });
20426
- return (0, __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.response)(resultCode || 414, errorMessage, "");
20824
+ return (0, __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.response)(414, errorMessage, "");
20427
20825
  } catch (error) {
20428
20826
  const handledError = Http.handleApiError(error);
20429
20827
  const errorMsg = handledError.message || "发布失败,请稍后重试";
20430
- const errorCode = handledError.code || 414;
20431
20828
  task.logger.error(`[shipinhaoPublishVideo] 发布流程异常 [${currentStep}]: ${errorMsg}`, stringifyError(error), handledError.extra);
20432
- task.logger.error(`[shipinhaoPublishVideo] 错误码: ${errorCode}, 当前步骤: ${currentStep}`);
20433
20829
  await updateTaskState?.({
20434
20830
  state: __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.TaskState.FAILED,
20435
20831
  error: errorMsg
20436
20832
  });
20437
- return (0, __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.response)(errorCode, errorMsg, "");
20833
+ return (0, __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.response)(414, errorMsg, "");
20438
20834
  }
20439
20835
  };
20440
20836
  const shipinhaoPublishVideo_rpa_rpaAction = async (task, params)=>{
20441
20837
  task.logger.info("开始微信视频号视频发布(RPA 模式)");
20442
- const videoMeta = parseVideoMeta(params.videoPath);
20443
- if (!videoMeta) return (0, __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.response)(414, "视频文件解析失败,请检查文件格式(仅支持 MP4)", "");
20838
+ const videoMeta = buildVideoMetaFromParams(params.videoPath, params.videoMetadata);
20444
20839
  task.logger.info(`视频: ${videoMeta.width}×${videoMeta.height}, ${videoMeta.duration.toFixed(2)}s, ${(videoMeta.fileSize / 1024 / 1024).toFixed(2)}MB, codec=${videoMeta.codec || "unknown"}`);
20840
+ if (!videoMeta.codec) task.logger.warn("未能读取视频编码格式,跳过 H.264 预检,交由服务端判断");
20445
20841
  const validationError = validateShipinhaoVideo(params.videoPath, videoMeta);
20446
20842
  if (validationError) {
20447
20843
  task.logger.error(`视频校验未通过: ${validationError}`);
@@ -20453,6 +20849,17 @@ const shipinhaoPublishVideo_rpa_rpaAction = async (task, params)=>{
20453
20849
  task.logger.error(`标题校验未通过: ${titleError}`);
20454
20850
  return (0, __WEBPACK_EXTERNAL_MODULE__iflyrpa_share_f7afdc8c__.response)(414, titleError, "");
20455
20851
  }
20852
+ const unsupported = [
20853
+ params.verticalCoverPath && "verticalCoverPath",
20854
+ params.collection && "collection",
20855
+ params.originalFlag && "originalFlag",
20856
+ params.postWithMemberZoneLink && "postWithMemberZoneLink"
20857
+ ].filter(Boolean);
20858
+ if (unsupported.length) task.logger.warn(`RPA 模式不支持以下参数,将被忽略: ${unsupported.join("、")};如需生效请使用 mockApi 模式`);
20859
+ if (params.tagInfo && 5 === params.tagInfo.tagType) {
20860
+ const shootInfo = params.tagInfo.shootInfo;
20861
+ if (shootInfo && (shootInfo.provinceCode || shootInfo.cityCode)) task.logger.warn("tagInfo.tagType=5 在 RPA 模式下仅支持拍摄时间和国家选择,省市选择需要使用 mockApi 模式");
20862
+ }
20456
20863
  const tmpCachePath = task.getTmpPath();
20457
20864
  const page = await task.createPage({
20458
20865
  url: "https://channels.weixin.qq.com/platform/post/create",
@@ -20563,7 +20970,12 @@ const shipinhaoPublishVideo_rpa_rpaAction = async (task, params)=>{
20563
20970
  }
20564
20971
  });
20565
20972
  }
20566
- if (params.description) {
20973
+ const descriptionText = payload_buildDescription({
20974
+ description: params.description,
20975
+ topics: params.topics,
20976
+ mentionedUsers: params.mentionedUsers
20977
+ });
20978
+ if (descriptionText) {
20567
20979
  task.logger.info("填写视频描述");
20568
20980
  await retryAction(async ()=>{
20569
20981
  const descEditor = await waitForElement(".input-editor", 10000);
@@ -20575,7 +20987,7 @@ const shipinhaoPublishVideo_rpa_rpaAction = async (task, params)=>{
20575
20987
  });
20576
20988
  await page.waitForTimeout(300);
20577
20989
  task.logger.info("已清空编辑器内容");
20578
- await descEditor.pressSequentially(params.description, {
20990
+ await descEditor.pressSequentially(descriptionText, {
20579
20991
  delay: 10
20580
20992
  });
20581
20993
  await page.waitForTimeout(500);
@@ -20610,7 +21022,59 @@ const shipinhaoPublishVideo_rpa_rpaAction = async (task, params)=>{
20610
21022
  await poperInstance.nth(1).click();
20611
21023
  task.logger.info("地点选择完成");
20612
21024
  }
20613
- if (params.scheduleTime) {
21025
+ if (params.collection) {
21026
+ task.logger.info(`选择合集: ${params.collection.collectionName}`);
21027
+ const instanceCollection = page.locator(".post-album-display-wrap");
21028
+ await instanceCollection.click();
21029
+ await page.waitForTimeout(1000);
21030
+ page.locator(".post-album-wrap .option-item").filter({
21031
+ hasText: params.collection.collectionName
21032
+ }).first().click({
21033
+ force: true
21034
+ });
21035
+ }
21036
+ if (params.link) {
21037
+ task.logger.info(`设置扩展阅读链接: ${params.link.title}`);
21038
+ await page.locator(".post-link-wrap .link-display-wrap").click();
21039
+ await page.waitForTimeout(300);
21040
+ const linkTypeText = 2 === params.link.urlType ? "红包封面" : "公众号文章";
21041
+ await page.locator(".link-option-item .title-wrap span").filter({
21042
+ hasText: linkTypeText
21043
+ }).click();
21044
+ await page.waitForTimeout(300);
21045
+ const placeholder = 2 === params.link.urlType ? "粘贴红包封面链接" : "粘贴公众号文章链接";
21046
+ await page.locator(`.link-input-wrap input[placeholder="${placeholder}"]`).fill(params.link.link);
21047
+ await page.waitForTimeout(500);
21048
+ task.logger.info(`已设置${linkTypeText}: ${params.link.link}`);
21049
+ }
21050
+ if (params.event) {
21051
+ task.logger.info(`选择活动: ${params.event.eventName}`);
21052
+ await page.locator(".post-activity-wrap .activity-display").click();
21053
+ await page.waitForTimeout(500);
21054
+ await page.locator(".activity-filter-wrap .weui-desktop-form__input[placeholder='搜索活动']").fill(params.event.eventName);
21055
+ await page.waitForTimeout(500);
21056
+ const searchLoading = page.locator(".search-loading");
21057
+ await searchLoading.waitFor({
21058
+ state: "hidden",
21059
+ timeout: 5000
21060
+ }).catch(()=>{
21061
+ task.logger.warn("活动搜索加载超时,继续尝试选择");
21062
+ });
21063
+ const activityItem = page.locator(".option-item .activity-item .activity-item-info .name").filter({
21064
+ hasText: params.event.eventName
21065
+ });
21066
+ const count = await activityItem.count();
21067
+ if (count > 0) {
21068
+ await activityItem.first().click();
21069
+ await page.waitForTimeout(300);
21070
+ task.logger.info(`已选择活动: ${params.event.eventName}`);
21071
+ } else {
21072
+ task.logger.warn(`未找到活动: ${params.event.eventName},将不参与活动`);
21073
+ await page.locator(".post-activity-wrap .activity-display").click();
21074
+ await page.waitForTimeout(300);
21075
+ }
21076
+ }
21077
+ if (params.scheduledTime) {
20614
21078
  task.logger.info("设置定时发布");
20615
21079
  const timingRadio = page.locator(".weui-desktop-form__check-label").filter({
20616
21080
  hasNotText: "不定时"
@@ -20619,10 +21083,10 @@ const shipinhaoPublishVideo_rpa_rpaAction = async (task, params)=>{
20619
21083
  await page.waitForTimeout(500);
20620
21084
  const instance = page.locator(".weui-desktop-picker__date");
20621
21085
  await instance.click();
20622
- const dateD = utils_TimeFormatter.format(1000 * params.scheduleTime, "d");
21086
+ const dateD = utils_TimeFormatter.format(1000 * params.scheduledTime, "d");
20623
21087
  const nowMonth = utils_TimeFormatter.format(Date.now(), "MM月");
20624
21088
  const nowMonthText = utils_TimeFormatter.format(Date.now(), "M月");
20625
- const month = utils_TimeFormatter.format(1000 * params.scheduleTime, "MM月");
21089
+ const month = utils_TimeFormatter.format(1000 * params.scheduledTime, "MM月");
20626
21090
  const monthLocator = await page.locator("weui-desktop-picker__panel__label").filter({
20627
21091
  hasText: month
20628
21092
  }).first();
@@ -20639,14 +21103,85 @@ const shipinhaoPublishVideo_rpa_rpaAction = async (task, params)=>{
20639
21103
  await page.locator(".weui-desktop-picker__table-row td a").filter({
20640
21104
  hasText: dateD
20641
21105
  }).first().click();
20642
- await page.locator(".weui-desktop-form__input-wrp input[placeholder*='请选择时间']").fill(utils_TimeFormatter.format(1000 * params.scheduleTime, "hh:mm"));
21106
+ await page.locator(".weui-desktop-form__input-wrp input[placeholder*='请选择时间']").fill(utils_TimeFormatter.format(1000 * params.scheduledTime, "hh:mm"));
20643
21107
  await page.locator("i.weui-desktop-icon__time").click();
20644
21108
  await page.locator(".post-time-wrap .form-item .label").filter({
20645
21109
  hasText: "发表时间"
20646
21110
  }).click();
20647
21111
  }
21112
+ if (params.tagInfo) {
21113
+ task.logger.info(`设置视频标注: tagType=${params.tagInfo.tagType}`);
21114
+ await page.locator(".mark-tag-select").click();
21115
+ await page.waitForTimeout(300);
21116
+ const tagTypeTextMap = {
21117
+ 0: "无需标注",
21118
+ 1: "含AI生成内容",
21119
+ 2: "内容包含营销广告",
21120
+ 3: "内容为虚构剧情,仅供娱乐",
21121
+ 5: "内容为自行拍摄",
21122
+ 7: "内容为转载",
21123
+ 8: "个人观点,仅供参考"
21124
+ };
21125
+ const tagText = tagTypeTextMap[params.tagInfo.tagType];
21126
+ if (tagText) {
21127
+ await page.locator(".mark-tag-option .option-main").filter({
21128
+ hasText: tagText
21129
+ }).click();
21130
+ await page.waitForTimeout(300);
21131
+ if (5 === params.tagInfo.tagType) {
21132
+ const shootInfo = params.tagInfo.shootInfo;
21133
+ if (shootInfo) {
21134
+ task.logger.info("填写拍摄时间和地点...");
21135
+ await page.waitForTimeout(500);
21136
+ if (shootInfo.postTimestamp) {
21137
+ task.logger.info(`设置拍摄时间: ${shootInfo.postTimestamp}`);
21138
+ const timestamp = 1000 * parseInt(shootInfo.postTimestamp, 10);
21139
+ const date = new Date(timestamp);
21140
+ await page.locator(".original-dialog-content .weui-desktop-picker__date input[placeholder*='请选择拍摄时间']").click();
21141
+ await page.waitForTimeout(300);
21142
+ const dayNum = date.getDate();
21143
+ await page.locator(".weui-desktop-picker__table a").filter({
21144
+ hasText: new RegExp(`^\\s*${dayNum}\\s*$`)
21145
+ }).first().click();
21146
+ await page.waitForTimeout(300);
21147
+ }
21148
+ if (shootInfo.countryCode || shootInfo.provinceCode || shootInfo.cityCode) {
21149
+ task.logger.info("设置拍摄地点...");
21150
+ await page.locator(".original-dialog-content .weui-desktop-form__dropdowncascade .weui-desktop-form__dropdowncascade__dt").click();
21151
+ await page.waitForTimeout(300);
21152
+ if (1156 === shootInfo.countryCode) {
21153
+ await page.locator(".weui-desktop-dropdown__list-ele .weui-desktop-dropdown__list-ele__text").filter({
21154
+ hasText: "中国"
21155
+ }).click();
21156
+ await page.waitForTimeout(300);
21157
+ task.logger.warn("RPA 模式下暂不支持选择具体省份和城市,仅选择了国家");
21158
+ } else task.logger.warn(`不支持的国家代码: ${shootInfo.countryCode},跳过地点设置`);
21159
+ }
21160
+ const confirmBtn = await waitForElement(".weui-desktop-dialog .weui-desktop-btn_primary");
21161
+ if (confirmBtn) {
21162
+ await confirmBtn.click();
21163
+ await page.waitForTimeout(300);
21164
+ }
21165
+ } else task.logger.warn("tagType=5 需要提供 shootInfo 字段(拍摄时间和地点)");
21166
+ }
21167
+ if (7 === params.tagInfo.tagType) {
21168
+ const repostSource = params.tagInfo.repostSource;
21169
+ if (repostSource) {
21170
+ task.logger.info(`填写转载来源: ${repostSource}`);
21171
+ await page.waitForTimeout(500);
21172
+ await page.locator(".repost-dialog-content .repost-textarea").fill(repostSource);
21173
+ await page.waitForTimeout(300);
21174
+ const confirmBtn = await waitForElement(".weui-desktop-dialog .weui-desktop-btn_primary");
21175
+ if (confirmBtn) {
21176
+ await confirmBtn.click();
21177
+ await page.waitForTimeout(300);
21178
+ }
21179
+ } else task.logger.warn("tagType=7 需要提供 repostSource 字段(转载来源)");
21180
+ }
21181
+ } else task.logger.warn(`未知的 tagType: ${params.tagInfo.tagType},跳过标注设置`);
21182
+ }
20648
21183
  task.logger.info("准备发布...");
20649
- await page.waitForTimeout(300);
21184
+ await page.waitForTimeout(500);
20650
21185
  let videoId = "";
20651
21186
  const handleResponse = async (response)=>{
20652
21187
  const url = response.url();
@@ -20712,19 +21247,60 @@ const shipinhaoPublishVideo_rpa_rpaAction = async (task, params)=>{
20712
21247
  };
20713
21248
  const ShipinhaoPublishVideoParamsSchema = ActionCommonParamsSchema.extend({
20714
21249
  videoPath: schemas_string().min(1),
21250
+ videoMetadata: schemas_object({
21251
+ duration: schemas_number().positive(),
21252
+ width: schemas_number().int().positive(),
21253
+ height: schemas_number().int().positive(),
21254
+ fileSize: schemas_number().int().positive(),
21255
+ path: schemas_string().min(1).optional(),
21256
+ fileName: schemas_string().min(1)
21257
+ }),
20715
21258
  coverPath: schemas_string().min(1),
21259
+ verticalCoverPath: schemas_string().min(1).optional(),
20716
21260
  description: schemas_string(),
20717
21261
  title: schemas_string().optional(),
20718
- scheduleTime: schemas_number().int().positive().optional(),
21262
+ scheduledTime: schemas_number().int().positive().optional(),
21263
+ isImmediatelyPublish: schemas_boolean().optional(),
21264
+ topics: schemas_array(schemas_string()).optional(),
21265
+ mentionedUsers: schemas_array(schemas_object({
21266
+ nickname: schemas_string()
21267
+ })).optional(),
21268
+ collection: schemas_object({
21269
+ collectionId: schemas_string(),
21270
+ collectionName: schemas_string()
21271
+ }).optional(),
21272
+ event: schemas_object({
21273
+ eventTopicId: schemas_string(),
21274
+ eventName: schemas_string(),
21275
+ eventCreatorNickname: schemas_string().optional()
21276
+ }).optional(),
21277
+ link: schemas_object({
21278
+ link: schemas_string(),
21279
+ title: schemas_string(),
21280
+ urlType: schemas_number().int().default(1)
21281
+ }).optional(),
21282
+ tagInfo: looseObject({
21283
+ tagType: schemas_number().int()
21284
+ }).optional(),
21285
+ originalFlag: union([
21286
+ literal(0),
21287
+ literal(1)
21288
+ ]).optional(),
21289
+ postWithMemberZoneLink: union([
21290
+ literal(0),
21291
+ literal(1)
21292
+ ]).optional(),
20719
21293
  location: schemas_object({
20720
21294
  latitude: schemas_number(),
20721
21295
  longitude: schemas_number(),
20722
21296
  city: schemas_string(),
21297
+ poiName: schemas_string().optional(),
21298
+ address: schemas_string().optional(),
20723
21299
  poiClassifyId: schemas_string().optional()
20724
21300
  }).optional()
20725
21301
  });
20726
21302
  const shipinhaoPublishVideo = async (task, params)=>{
20727
- task.logger.info(`shipinhaoPublishVideo actionType: ${params.actionType}`);
21303
+ task.logger.info(`[shipinhaoPublishVideo] actionType: ${params.actionType}`);
20728
21304
  if ("rpa" === params.actionType) return shipinhaoPublishVideo_rpa_rpaAction(task, params);
20729
21305
  if ("mockApi" === params.actionType) return shipinhaoPublishVideo_mock_mockAction(task, params);
20730
21306
  return executeAction(shipinhaoPublishVideo_mock_mockAction, shipinhaoPublishVideo_rpa_rpaAction)(task, params);
@@ -26822,4 +27398,4 @@ var __webpack_exports__version = package_namespaceObject.i8;
26822
27398
  export { Action, ActionCommonParamsSchema, BaijiahaoPublishParamsSchema, BetaFlag, CollectionDetailSchema, ConfigDataSchema, DouyinCheckVerifyQrCodeParamsSchema, DouyinCreateCommentReplyParamsSchema, DouyinGetCollectionParamsSchema, DouyinGetCommentListParamsSchema, DouyinGetCommentReplyListParamsSchema, DouyinGetHotParamsSchema, DouyinGetLocationParamsSchema, DouyinGetMusicByCategoryParamsSchema, DouyinGetMusicCategoryParamsSchema, DouyinGetMusicParamsSchema, DouyinGetTopicsParamsSchema, DouyinGetVerifyQrCodeParamsSchema, DouyinGetWorkListParamsSchema, DouyinPublishParamsSchema, FetchArticlesDataSchema, FetchArticlesParamsSchema, Http, ProxyAgent, SearchAccountInfoParamsSchema, SessionCheckResultSchema, ShipinhaoCheckLinkValidateParamsSchema, ShipinhaoGetLocationParamsSchema, ShipinhaoGetMsgParamsSchema, ShipinhaoPublishParamsSchema, ShipinhaoPublishVideoParamsSchema, ShipinhaoSendMsgParamsSchema, ToutiaoPublishParamsSchema, UnreadCountSchema, WeixinPublishParamsSchema, WxBjhSessionParamsSchema, XhsWebSearchParamsSchema, XiaohongshuPublishParamsSchema, bjhConfigDataSchema, douyinConfigDataSchema, getFileState, reportLogger, rpaAction_Server_Mock, shipinhaoConfigDataSchema, ttConfigDataSchema, wxConfigDataSchema, xhsConfigDataSchema, __webpack_exports__version as version };
26823
27399
 
26824
27400
  //# sourceMappingURL=index.mjs.map
26825
- //# debugId=19f291bd-1860-5dc2-abcf-dd482b04a403
27401
+ //# debugId=309d0c4f-ece2-5152-b000-0390cb565a64