tt-help-cli-ycl 1.4.10 → 1.4.11
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/package.json +1 -1
- package/src/cli/comments.js +112 -15
package/package.json
CHANGED
package/src/cli/comments.js
CHANGED
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
// 手动模式: tt-help comments <URL> [N] [--save] 单视频评论提取
|
|
6
6
|
// ────────────────────────────────────────
|
|
7
7
|
|
|
8
|
+
import { writeFileSync, unlinkSync } from "fs";
|
|
8
9
|
import os from "os";
|
|
10
|
+
import path from "path";
|
|
9
11
|
import { execSync } from "child_process";
|
|
10
12
|
|
|
11
13
|
import { fetchUserCommentsAPI } from "../lib/api-interceptor-comment.js";
|
|
@@ -65,6 +67,8 @@ const CFG = {
|
|
|
65
67
|
periodicRecycleEvery: 0,
|
|
66
68
|
/** 内存回收 — 回收时是否切换端口 */
|
|
67
69
|
switchPortOnRecycle: true,
|
|
70
|
+
/** 连续任务处理错误阈值(达到后上报服务端) */
|
|
71
|
+
consecutiveTaskErrorsMax: 20,
|
|
68
72
|
};
|
|
69
73
|
|
|
70
74
|
/** 从环境变量覆写配置 */
|
|
@@ -97,6 +101,10 @@ function loadEnvConfig() {
|
|
|
97
101
|
);
|
|
98
102
|
CFG.switchPortOnRecycle =
|
|
99
103
|
String(process.env.COMMENTS_SWITCH_PORT_ON_RECYCLE || "1") !== "0";
|
|
104
|
+
CFG.consecutiveTaskErrorsMax = Math.max(
|
|
105
|
+
1,
|
|
106
|
+
int("COMMENTS_CONSECUTIVE_TASK_ERRORS_MAX", CFG.consecutiveTaskErrorsMax),
|
|
107
|
+
);
|
|
100
108
|
}
|
|
101
109
|
|
|
102
110
|
// ────────────────────────────────────────
|
|
@@ -108,32 +116,82 @@ function formatMemoryUsage(mem = process.memoryUsage()) {
|
|
|
108
116
|
return `rss:${mb(mem.rss)}MB heap:${mb(mem.heapUsed)}MB ext:${mb(mem.external)}MB ab:${mb(mem.arrayBuffers)}MB`;
|
|
109
117
|
}
|
|
110
118
|
|
|
111
|
-
|
|
119
|
+
/**
|
|
120
|
+
* 获取 Edge 浏览器进程的内存占用(跨平台:macOS/Linux/Windows)
|
|
121
|
+
*/
|
|
122
|
+
function getBrowserMemory(port = null) {
|
|
112
123
|
try {
|
|
113
124
|
const platform = os.platform();
|
|
114
|
-
if (platform !== "darwin" && platform !== "linux") return null;
|
|
115
|
-
const procName = platform === "darwin" ? "Microsoft Edge" : "msedge";
|
|
116
|
-
const c = procName[0];
|
|
117
|
-
const rest = procName.slice(1);
|
|
118
125
|
let cmd;
|
|
119
|
-
if (
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
126
|
+
if (platform === "darwin") {
|
|
127
|
+
if (port) {
|
|
128
|
+
cmd = `ps aux | grep "[M]icrosoft Edge" | grep "remote-debugging-port=${port}" | awk '{sum += $6} END {printf "%d", sum/1024}'`;
|
|
129
|
+
} else {
|
|
130
|
+
cmd =
|
|
131
|
+
'ps aux | grep "[M]icrosoft Edge" | awk \'{sum += $6} END {printf "%d", sum/1024}\'';
|
|
132
|
+
}
|
|
133
|
+
const result = execSync(cmd, { timeout: 5000 }).toString().trim();
|
|
134
|
+
const mb = parseInt(result, 10);
|
|
135
|
+
return mb > 0 ? mb : null;
|
|
136
|
+
} else if (platform === "linux") {
|
|
137
|
+
if (port) {
|
|
138
|
+
cmd = `ps aux | grep "[m]sedge" | grep "remote-debugging-port=${port}" | awk '{sum += $6} END {printf "%d", sum/1024}'`;
|
|
139
|
+
} else {
|
|
140
|
+
cmd =
|
|
141
|
+
'ps aux | grep "[m]sedge" | awk \'{sum += $6} END {printf "%d", sum/1024}\'';
|
|
142
|
+
}
|
|
143
|
+
const result = execSync(cmd, { timeout: 5000 }).toString().trim();
|
|
144
|
+
const mb = parseInt(result, 10);
|
|
145
|
+
return mb > 0 ? mb : null;
|
|
146
|
+
} else if (platform === "win32") {
|
|
147
|
+
const ps1Path = path.join(os.tmpdir(), `edge-mem-${Date.now()}.ps1`);
|
|
148
|
+
try {
|
|
149
|
+
let ps1Content;
|
|
150
|
+
if (port) {
|
|
151
|
+
ps1Content =
|
|
152
|
+
`$procs = Get-CimInstance Win32_Process -Filter "Name LIKE 'msedge%'" -ErrorAction SilentlyContinue; ` +
|
|
153
|
+
`$sum = 0; ` +
|
|
154
|
+
`foreach ($p in $procs) { ` +
|
|
155
|
+
` if ($p.CommandLine -and $p.CommandLine -like "*--remote-debugging-port=${port}*") { ` +
|
|
156
|
+
` $sum += $p.WorkingSetSize ` +
|
|
157
|
+
` } ` +
|
|
158
|
+
`}; ` +
|
|
159
|
+
`Write-Output $sum`;
|
|
160
|
+
} else {
|
|
161
|
+
ps1Content =
|
|
162
|
+
`$procs = Get-CimInstance Win32_Process -Filter "Name LIKE 'msedge%'" -ErrorAction SilentlyContinue; ` +
|
|
163
|
+
`$sum = 0; ` +
|
|
164
|
+
`foreach ($p in $procs) { $sum += $p.WorkingSetSize }; ` +
|
|
165
|
+
`Write-Output $sum`;
|
|
166
|
+
}
|
|
167
|
+
writeFileSync(ps1Path, ps1Content);
|
|
168
|
+
cmd = `powershell.exe -NoProfile -ExecutionPolicy Bypass -File "${ps1Path}"`;
|
|
169
|
+
const result = execSync(cmd, { timeout: 5000 }).toString().trim();
|
|
170
|
+
try {
|
|
171
|
+
unlinkSync(ps1Path);
|
|
172
|
+
} catch {}
|
|
173
|
+
const bytes = parseInt(result, 10);
|
|
174
|
+
const mb = bytes > 0 ? Math.round(bytes / 1048576) : 0;
|
|
175
|
+
return mb > 0 ? mb : null;
|
|
176
|
+
} catch {
|
|
177
|
+
try {
|
|
178
|
+
unlinkSync(ps1Path);
|
|
179
|
+
} catch {}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
123
182
|
}
|
|
124
|
-
|
|
125
|
-
return mb > 0 ? mb : null;
|
|
183
|
+
return null;
|
|
126
184
|
} catch {
|
|
127
185
|
return null;
|
|
128
186
|
}
|
|
129
187
|
}
|
|
130
188
|
|
|
131
|
-
function formatMemoryWithBrowser(port) {
|
|
189
|
+
function formatMemoryWithBrowser(port = null) {
|
|
132
190
|
const nodeMem = formatMemoryUsage();
|
|
133
191
|
const edgeMB = getBrowserMemory(port);
|
|
134
192
|
if (!edgeMB) return nodeMem;
|
|
135
|
-
const
|
|
136
|
-
return nodeMem
|
|
193
|
+
const portLabel = port ? `:${port}` : "";
|
|
194
|
+
return `${nodeMem} edge${portLabel}:${edgeMB}MB`;
|
|
137
195
|
}
|
|
138
196
|
|
|
139
197
|
// ────────────────────────────────────────
|
|
@@ -481,7 +539,8 @@ async function runAutoMode(options) {
|
|
|
481
539
|
browserRestartCount = 0;
|
|
482
540
|
let consecutiveErrors = 0,
|
|
483
541
|
consecutive403 = 0,
|
|
484
|
-
consecutiveCaptchaCount = 0
|
|
542
|
+
consecutiveCaptchaCount = 0,
|
|
543
|
+
consecutiveTaskErrors = 0;
|
|
485
544
|
|
|
486
545
|
// ── CDP 会话 ──
|
|
487
546
|
const session = new CdpSessionManager({
|
|
@@ -720,6 +779,7 @@ async function runAutoMode(options) {
|
|
|
720
779
|
await apiPut(`${serverUrl}/api/comment-task/${id}`);
|
|
721
780
|
processedCount++;
|
|
722
781
|
consecutive403 = 0;
|
|
782
|
+
consecutiveTaskErrors = 0;
|
|
723
783
|
|
|
724
784
|
// 每 10 个任务输出内存汇总
|
|
725
785
|
if (totalTasks() % 10 === 0) {
|
|
@@ -729,6 +789,7 @@ async function runAutoMode(options) {
|
|
|
729
789
|
}
|
|
730
790
|
} catch (err) {
|
|
731
791
|
errorCount++;
|
|
792
|
+
consecutiveTaskErrors++;
|
|
732
793
|
const isBrowserClosed = isBrowserClosedError(err);
|
|
733
794
|
const is403 =
|
|
734
795
|
err.message &&
|
|
@@ -754,6 +815,22 @@ async function runAutoMode(options) {
|
|
|
754
815
|
} else {
|
|
755
816
|
console.error(` ${err.message}`);
|
|
756
817
|
}
|
|
818
|
+
|
|
819
|
+
// 连续任务错误达到阈值,汇报服务端
|
|
820
|
+
if (consecutiveTaskErrors >= CFG.consecutiveTaskErrorsMax) {
|
|
821
|
+
console.error(
|
|
822
|
+
` ⚠️ 连续任务错误 ${consecutiveTaskErrors}/${CFG.consecutiveTaskErrorsMax} → 汇报服务端`,
|
|
823
|
+
);
|
|
824
|
+
apiPost(`${serverUrl}/api/error-report`, {
|
|
825
|
+
userId,
|
|
826
|
+
username: "",
|
|
827
|
+
errorType: "consecutive_task_errors",
|
|
828
|
+
errorMessage: `连续 ${consecutiveTaskErrors} 个任务处理失败 (port=${session.port})`,
|
|
829
|
+
stage: "comments",
|
|
830
|
+
errorStack: "",
|
|
831
|
+
}).catch(() => {});
|
|
832
|
+
consecutiveTaskErrors = 0;
|
|
833
|
+
}
|
|
757
834
|
}
|
|
758
835
|
|
|
759
836
|
// 任务间内存检查
|
|
@@ -761,6 +838,7 @@ async function runAutoMode(options) {
|
|
|
761
838
|
}
|
|
762
839
|
} catch (err) {
|
|
763
840
|
errorCount++;
|
|
841
|
+
consecutiveTaskErrors++;
|
|
764
842
|
if (isBrowserClosedError(err)) {
|
|
765
843
|
browserRestartCount++;
|
|
766
844
|
console.error(` ⚠ 浏览器断开 (#${browserRestartCount}) → 重启`);
|
|
@@ -768,6 +846,22 @@ async function runAutoMode(options) {
|
|
|
768
846
|
} else {
|
|
769
847
|
console.error(` × ${err.message}`);
|
|
770
848
|
}
|
|
849
|
+
|
|
850
|
+
// 连续轮间错误达到阈值,汇报服务端
|
|
851
|
+
if (consecutiveTaskErrors >= CFG.consecutiveTaskErrorsMax) {
|
|
852
|
+
console.error(
|
|
853
|
+
` ⚠️ 连续轮间错误 ${consecutiveTaskErrors}/${CFG.consecutiveTaskErrorsMax} → 汇报服务端`,
|
|
854
|
+
);
|
|
855
|
+
apiPost(`${serverUrl}/api/error-report`, {
|
|
856
|
+
userId,
|
|
857
|
+
username: "",
|
|
858
|
+
errorType: "consecutive_task_errors",
|
|
859
|
+
errorMessage: `连续 ${consecutiveTaskErrors} 个轮次处理失败 (port=${session.port})`,
|
|
860
|
+
stage: "comments",
|
|
861
|
+
errorStack: "",
|
|
862
|
+
}).catch(() => {});
|
|
863
|
+
consecutiveTaskErrors = 0;
|
|
864
|
+
}
|
|
771
865
|
}
|
|
772
866
|
|
|
773
867
|
// 轮间内存检查 + 等待
|
|
@@ -794,7 +888,9 @@ async function runManualMode(url, options) {
|
|
|
794
888
|
save: shouldSave,
|
|
795
889
|
serverUrl = defaultServer,
|
|
796
890
|
basePort = 9222,
|
|
891
|
+
proxy: proxyOption,
|
|
797
892
|
} = options;
|
|
893
|
+
const effectiveProxy = proxyOption || configuredProxy;
|
|
798
894
|
|
|
799
895
|
let browser;
|
|
800
896
|
try {
|
|
@@ -930,5 +1026,6 @@ export async function handleComments(options) {
|
|
|
930
1026
|
save: commentsSave,
|
|
931
1027
|
serverUrl: commentsServer || defaultServer,
|
|
932
1028
|
basePort: commentsBasePort,
|
|
1029
|
+
proxy: commentsProxy,
|
|
933
1030
|
});
|
|
934
1031
|
}
|