tt-help-cli-ycl 1.3.82 → 1.3.84
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/attach.js +3 -34
- package/src/cli/auto.js +3 -18
- package/src/cli/comments.js +13 -57
- package/src/cli/explore.js +281 -269
- package/src/cli/refresh.js +6 -21
- package/src/lib/api-client.js +101 -0
- package/src/lib/api-interceptor-comment.js +56 -14
- package/src/lib/api-interceptor.js +11 -1
- package/src/watch/data-store.js +36 -0
- package/src/watch/public/app.js +96 -1
- package/src/watch/public/index.html +15 -0
- package/src/watch/public/style.css +107 -0
- package/src/watch/server.js +22 -0
package/package.json
CHANGED
package/src/cli/attach.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { TikTokScraper } from "../lib/tiktok-scraper.mjs";
|
|
2
2
|
import { CDNBlockedError } from "../lib/parse-ssr.mjs";
|
|
3
3
|
import { proxy as configuredProxy } from "../lib/constants.js";
|
|
4
|
+
import { createApiClient } from "../lib/api-client.js";
|
|
4
5
|
import v8 from "node:v8";
|
|
5
6
|
|
|
6
|
-
const MAX_RETRY_WAIT = 5 * 60 * 1000;
|
|
7
7
|
const HEAP_RESTART_RATIO = 0.72;
|
|
8
8
|
const MAX_TASK_BATCHES_BEFORE_RESTART = 200;
|
|
9
9
|
|
|
@@ -17,39 +17,6 @@ function attachLog(message = "") {
|
|
|
17
17
|
console.error(`[${formatNow()}] ${message}`);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
async function withRetry(label, fn) {
|
|
21
|
-
let backoff = 1000;
|
|
22
|
-
while (true) {
|
|
23
|
-
try {
|
|
24
|
-
return await fn();
|
|
25
|
-
} catch (err) {
|
|
26
|
-
attachLog(
|
|
27
|
-
`[连接] ${label} 失败: ${err.message},${backoff / 1000}秒后重试...`,
|
|
28
|
-
);
|
|
29
|
-
await new Promise((r) => setTimeout(r, backoff));
|
|
30
|
-
if (backoff < MAX_RETRY_WAIT) backoff *= 2;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function apiGet(url) {
|
|
36
|
-
return withRetry(`GET ${url}`, async () => {
|
|
37
|
-
const res = await fetch(url);
|
|
38
|
-
return res.json();
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function apiPost(url, body) {
|
|
43
|
-
return withRetry(`POST ${url}`, async () => {
|
|
44
|
-
const res = await fetch(url, {
|
|
45
|
-
method: "POST",
|
|
46
|
-
headers: { "Content-Type": "application/json" },
|
|
47
|
-
body: JSON.stringify(body),
|
|
48
|
-
});
|
|
49
|
-
return res.json();
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
|
|
53
20
|
function isBrowserClosedError(err) {
|
|
54
21
|
if (!err) return false;
|
|
55
22
|
const msg = err.message || err.toString() || "";
|
|
@@ -143,6 +110,8 @@ export async function handleAttach(options) {
|
|
|
143
110
|
`[Attach] 并行数: ${attachParallel}, 空闲间隔: ${attachInterval}秒, 服务端: ${serverUrl}${countryStr}`,
|
|
144
111
|
);
|
|
145
112
|
|
|
113
|
+
const { apiGet, apiPost } = createApiClient({ log: false });
|
|
114
|
+
|
|
146
115
|
const scraper = new TikTokScraper({
|
|
147
116
|
poolSize: attachPoolSize || 3,
|
|
148
117
|
proxyServer: effectiveProxy || null,
|
package/src/cli/auto.js
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
import { userId as configuredUserId, saveUserId } from "../lib/constants.js";
|
|
7
7
|
import { getMacOrUuid } from "../lib/mac-or-uuid.js";
|
|
8
8
|
import { ensureBrowserReady as ensureBrowserReadyCDP } from "../lib/browser/cdp.js";
|
|
9
|
+
import { createApiClient } from "../lib/api-client.js";
|
|
9
10
|
|
|
10
11
|
const MAX_RETRY_WAIT = 5 * 60 * 1000;
|
|
11
12
|
|
|
@@ -24,24 +25,6 @@ async function withRetry(label, fn) {
|
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
async function apiPost(url, body) {
|
|
28
|
-
return withRetry(`POST ${url}`, async () => {
|
|
29
|
-
const res = await fetch(url, {
|
|
30
|
-
method: "POST",
|
|
31
|
-
headers: { "Content-Type": "application/json" },
|
|
32
|
-
body: JSON.stringify(body),
|
|
33
|
-
});
|
|
34
|
-
return res.json();
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async function apiGet(url) {
|
|
39
|
-
return withRetry(`GET ${url}`, async () => {
|
|
40
|
-
const res = await fetch(url);
|
|
41
|
-
return res.json();
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
28
|
export async function handleAuto(options) {
|
|
46
29
|
const {
|
|
47
30
|
autoUsernames,
|
|
@@ -87,6 +70,8 @@ export async function handleAuto(options) {
|
|
|
87
70
|
console.error(`[初始化] 未检测到本地用户编号,已生成并使用: ${userId}`);
|
|
88
71
|
}
|
|
89
72
|
|
|
73
|
+
const { apiGet, apiPost } = createApiClient();
|
|
74
|
+
|
|
90
75
|
const runOptions = {
|
|
91
76
|
collectMax: autoCollectMax,
|
|
92
77
|
scrapeDepth: autoScrapeDepth,
|
package/src/cli/comments.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
isLocationInList,
|
|
8
8
|
normalizeLocation,
|
|
9
9
|
} from "../lib/target-locations.js";
|
|
10
|
+
import { createApiClient } from "../lib/api-client.js";
|
|
10
11
|
|
|
11
12
|
async function waitForPageReady(page, timeout = 30000) {
|
|
12
13
|
const startTime = Date.now();
|
|
@@ -32,64 +33,7 @@ async function safeEvaluate(page, fn) {
|
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
async function withRetry(label, fn, maxRetries = 3) {
|
|
36
|
-
let backoff = 2000;
|
|
37
|
-
for (let i = 0; i < maxRetries; i++) {
|
|
38
|
-
try {
|
|
39
|
-
return await fn();
|
|
40
|
-
} catch (err) {
|
|
41
|
-
if (i < maxRetries - 1) {
|
|
42
|
-
console.error(
|
|
43
|
-
` [${label}] 失败: ${err.message},${backoff / 1000}s 后重试...`,
|
|
44
|
-
);
|
|
45
|
-
await new Promise((r) => setTimeout(r, backoff));
|
|
46
|
-
backoff *= 2;
|
|
47
|
-
} else {
|
|
48
|
-
throw err;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async function apiPost(url, body) {
|
|
55
|
-
return withRetry(`POST ${url}`, async () => {
|
|
56
|
-
const res = await fetch(url, {
|
|
57
|
-
method: "POST",
|
|
58
|
-
headers: { "Content-Type": "application/json" },
|
|
59
|
-
body: JSON.stringify(body),
|
|
60
|
-
});
|
|
61
|
-
if (!res.ok) {
|
|
62
|
-
const errText = await res.text();
|
|
63
|
-
throw new Error(`HTTP ${res.status}: ${errText.substring(0, 200)}`);
|
|
64
|
-
}
|
|
65
|
-
return res.json();
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
36
|
|
|
69
|
-
async function apiPut(url) {
|
|
70
|
-
return withRetry(`PUT ${url}`, async () => {
|
|
71
|
-
const res = await fetch(url, {
|
|
72
|
-
method: "PUT",
|
|
73
|
-
headers: { "Content-Type": "application/json" },
|
|
74
|
-
});
|
|
75
|
-
if (!res.ok) {
|
|
76
|
-
const errText = await res.text();
|
|
77
|
-
throw new Error(`HTTP ${res.status}: ${errText.substring(0, 200)}`);
|
|
78
|
-
}
|
|
79
|
-
return res.json();
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async function apiGet(url) {
|
|
84
|
-
return withRetry(`GET ${url}`, async () => {
|
|
85
|
-
const res = await fetch(url);
|
|
86
|
-
if (!res.ok) {
|
|
87
|
-
const errText = await res.text();
|
|
88
|
-
throw new Error(`HTTP ${res.status}: ${errText.substring(0, 200)}`);
|
|
89
|
-
}
|
|
90
|
-
return res.json();
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
37
|
|
|
94
38
|
function isBrowserClosedError(err) {
|
|
95
39
|
if (!err) return false;
|
|
@@ -110,6 +54,12 @@ function isBrowserClosedError(err) {
|
|
|
110
54
|
*/
|
|
111
55
|
async function runAutoMode(options) {
|
|
112
56
|
const { serverUrl, parallel, interval, maxComments } = options;
|
|
57
|
+
const { apiGet, apiPost, apiPut } = createApiClient({
|
|
58
|
+
checkStatus: true,
|
|
59
|
+
maxRetries: 2,
|
|
60
|
+
backoff: 2000,
|
|
61
|
+
log: true,
|
|
62
|
+
});
|
|
113
63
|
const actualParallel = Math.max(1, parallel || 1);
|
|
114
64
|
const actualInterval = interval || 10;
|
|
115
65
|
const actualMaxComments = maxComments || 200;
|
|
@@ -478,6 +428,12 @@ export async function handleComments(options) {
|
|
|
478
428
|
|
|
479
429
|
const guessedLocation = normalizeLocation(videoInfo?.locationCreated);
|
|
480
430
|
const serverUrl = commentsServer || defaultServer;
|
|
431
|
+
const { apiPost } = createApiClient({
|
|
432
|
+
checkStatus: true,
|
|
433
|
+
maxRetries: 2,
|
|
434
|
+
backoff: 2000,
|
|
435
|
+
log: true,
|
|
436
|
+
});
|
|
481
437
|
|
|
482
438
|
if (
|
|
483
439
|
guessedLocation &&
|