@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/actions/searchAccountInfo/index.d.ts +1 -1
- package/dist/actions/shipinhaoPublishVideo/index.d.ts +45 -1
- package/dist/actions/shipinhaoPublishVideo/payload.d.ts +70 -0
- package/dist/actions/shipinhaoPublishVideo/uploader.d.ts +19 -0
- package/dist/actions/shipinhaoPublishVideo/videoMeta.d.ts +43 -5
- package/dist/actions/shipinhaoPublishVideo/videoValidator.d.ts +4 -2
- package/dist/bundle.js +886 -310
- package/dist/bundle.js.map +1 -1
- package/dist/index.js +884 -308
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +884 -308
- package/dist/index.mjs.map +1 -1
- package/dist/utils/feishuAlarm.d.ts +4 -1
- package/dist/utils/http.d.ts +16 -0
- package/dist/utils/shipinhao/utils.d.ts +10 -0
- package/package.json +2 -2
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]="
|
|
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 =
|
|
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)
|
|
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
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
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 ??
|
|
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
|
-
|
|
4849
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
9960
|
-
timeout:
|
|
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:
|
|
11146
|
-
timeout:
|
|
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:
|
|
11179
|
-
timeout:
|
|
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:
|
|
15455
|
-
timeout:
|
|
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:
|
|
15883
|
-
timeout:
|
|
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:
|
|
15893
|
-
timeout:
|
|
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.
|
|
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
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
19859
|
-
|
|
19860
|
-
|
|
19861
|
-
|
|
19862
|
-
|
|
19863
|
-
|
|
19864
|
-
|
|
19865
|
-
|
|
19866
|
-
if (
|
|
19867
|
-
|
|
19868
|
-
|
|
19869
|
-
|
|
19870
|
-
|
|
19871
|
-
|
|
19872
|
-
|
|
19873
|
-
|
|
19874
|
-
|
|
19875
|
-
|
|
19876
|
-
|
|
19877
|
-
|
|
19878
|
-
|
|
19879
|
-
|
|
19880
|
-
|
|
19881
|
-
|
|
19882
|
-
|
|
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
|
-
|
|
19885
|
-
|
|
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
|
-
|
|
19888
|
-
|
|
19889
|
-
|
|
19890
|
-
|
|
19891
|
-
|
|
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
|
-
|
|
19895
|
-
|
|
19896
|
-
|
|
19897
|
-
|
|
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
|
-
|
|
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
|
-
|
|
19911
|
-
|
|
19912
|
-
|
|
19913
|
-
|
|
19914
|
-
|
|
19915
|
-
|
|
19916
|
-
|
|
19917
|
-
|
|
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
|
-
|
|
19921
|
-
|
|
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
|
|
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
|
-
|
|
20017
|
-
|
|
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:
|
|
20406
|
+
url: `${MICRO_CONTENT_BASE}/post/get-finder-post-trace-key`,
|
|
20407
|
+
params: buildPublishQuery(client),
|
|
20021
20408
|
data: {
|
|
20022
20409
|
objectId: "",
|
|
20023
|
-
|
|
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:
|
|
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
|
-
|
|
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:
|
|
20491
|
+
url: `${MICRO_CONTENT_BASE}/post/post_clip_video_result`,
|
|
20492
|
+
params: buildPublishQuery(client),
|
|
20098
20493
|
data: {
|
|
20099
20494
|
clipKey,
|
|
20100
20495
|
draftId,
|
|
20101
|
-
|
|
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)
|
|
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
|
-
|
|
20146
|
-
const
|
|
20147
|
-
|
|
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
|
-
|
|
20150
|
-
|
|
20151
|
-
|
|
20152
|
-
|
|
20153
|
-
|
|
20154
|
-
|
|
20155
|
-
|
|
20156
|
-
|
|
20157
|
-
|
|
20158
|
-
|
|
20159
|
-
|
|
20160
|
-
|
|
20161
|
-
|
|
20162
|
-
|
|
20163
|
-
|
|
20164
|
-
|
|
20165
|
-
|
|
20166
|
-
|
|
20167
|
-
|
|
20168
|
-
|
|
20169
|
-
|
|
20170
|
-
|
|
20171
|
-
|
|
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
|
-
|
|
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
|
-
|
|
20229
|
-
|
|
20230
|
-
|
|
20231
|
-
|
|
20232
|
-
|
|
20233
|
-
|
|
20234
|
-
|
|
20235
|
-
|
|
20236
|
-
|
|
20237
|
-
|
|
20238
|
-
|
|
20239
|
-
|
|
20240
|
-
|
|
20241
|
-
|
|
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]
|
|
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
|
|
20259
|
-
|
|
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
|
-
|
|
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
|
|
20308
|
-
|
|
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]
|
|
20326
|
-
currentStep = "
|
|
20327
|
-
task.
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
20392
|
-
task.logger.
|
|
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
|
-
|
|
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)(
|
|
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)(
|
|
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 =
|
|
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
|
-
|
|
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(
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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(
|
|
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
|
-
|
|
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=
|
|
27401
|
+
//# debugId=309d0c4f-ece2-5152-b000-0390cb565a64
|