tt-help-cli-ycl 1.3.45 → 1.3.46
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/README.md +33 -33
- package/cli.js +9 -9
- package/package.json +52 -52
- package/scripts/run-explore copy.bat +101 -101
- package/scripts/run-explore.bat +134 -134
- package/scripts/run-explore.ps1 +159 -159
- package/scripts/run-explore.sh +121 -121
- package/src/cli/attach.js +331 -313
- package/src/cli/auto.js +265 -265
- package/src/cli/comments.js +620 -620
- package/src/cli/config.js +170 -170
- package/src/cli/db-import.js +51 -51
- package/src/cli/explore.js +555 -555
- package/src/cli/info.js +10 -16
- package/src/cli/open.js +111 -111
- package/src/cli/progress.js +111 -111
- package/src/cli/refresh.js +288 -288
- package/src/cli/scrape.js +47 -47
- package/src/cli/utils.js +18 -18
- package/src/cli/videos.js +41 -41
- package/src/cli/videostats.js +196 -196
- package/src/cli/watch.js +30 -30
- package/src/cli/webserver.js +19 -0
- package/src/lib/api-interceptor.js +161 -161
- package/src/lib/args.js +809 -778
- package/src/lib/browser/anti-detect.js +23 -23
- package/src/lib/browser/cdp.js +261 -261
- package/src/lib/browser/health-checker.js +114 -114
- package/src/lib/browser/launch.js +43 -43
- package/src/lib/browser/page.js +184 -184
- package/src/lib/constants.js +297 -287
- package/src/lib/delay.js +54 -54
- package/src/lib/explore-fetch.js +118 -118
- package/src/lib/fetcher.js +45 -45
- package/src/lib/filter.js +66 -66
- package/src/lib/io.js +54 -54
- package/src/lib/output.js +80 -80
- package/src/lib/page-error-detector.js +109 -109
- package/src/lib/parse-ssr.mjs +69 -69
- package/src/lib/parser.js +47 -47
- package/src/lib/retry.js +45 -45
- package/src/lib/scrape.js +90 -89
- package/src/lib/target-locations.js +61 -61
- package/src/lib/tiktok-scraper.mjs +160 -106
- package/src/lib/url.js +52 -52
- package/src/main.js +73 -70
- package/src/npm-main.js +70 -69
- package/src/scraper/auto-core.js +203 -203
- package/src/scraper/core.js +255 -255
- package/src/scraper/explore-core.js +208 -208
- package/src/scraper/modules/captcha-handler.js +114 -114
- package/src/scraper/modules/follow-extractor.js +250 -250
- package/src/scraper/modules/guess-extractor.js +51 -51
- package/src/scraper/modules/page-helpers.js +48 -48
- package/src/scraper/refresh-core.js +213 -213
- package/src/videos/core.js +143 -143
- package/src/watch/data-store.js +2980 -2978
- package/src/watch/public/index.html +2355 -2345
- package/src/watch/server.js +727 -727
- package/src/webserver/server.mjs +174 -0
- package/scripts/test-captcha-lib.mjs +0 -68
- package/scripts/test-captcha.mjs +0 -81
- package/scripts/test-incognito-lib.mjs +0 -36
- package/scripts/test-login-state.mjs +0 -128
- package/scripts/test-safe-click.mjs +0 -45
- package/scripts/test-watch-db-smoke.mjs +0 -246
- package/src/results/user-videos-bar.lar.lar.moeta.json +0 -37
package/src/lib/constants.js
CHANGED
|
@@ -1,287 +1,297 @@
|
|
|
1
|
-
import { join, dirname } from "path";
|
|
2
|
-
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
3
|
-
import { fileURLToPath } from "url";
|
|
4
|
-
import { DEFAULT_TARGET_LOCATIONS_CSV } from "./target-locations.js";
|
|
5
|
-
|
|
6
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
-
const __dirname = dirname(__filename);
|
|
8
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
9
|
-
const configPath = join(homeDir, ".tt-help.json");
|
|
10
|
-
|
|
11
|
-
const DEFAULT_PROXY = "http://127.0.0.1:7897";
|
|
12
|
-
const DEFAULT_OUTPUT = "tiktok_data.json";
|
|
13
|
-
|
|
14
|
-
let proxy = DEFAULT_PROXY;
|
|
15
|
-
let server = "http://127.0.0.1:3001";
|
|
16
|
-
let configFile = null;
|
|
17
|
-
let browser = null;
|
|
18
|
-
let userId = null;
|
|
19
|
-
let maxFollowing = 50;
|
|
20
|
-
let maxFollowers = 50;
|
|
21
|
-
let maxVideos = 16;
|
|
22
|
-
let maxComments = 10;
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
if (existsSync(configPath)) {
|
|
26
|
-
const cfg = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
27
|
-
if (cfg.proxy) {
|
|
28
|
-
proxy = cfg.proxy;
|
|
29
|
-
}
|
|
30
|
-
if (cfg.server) {
|
|
31
|
-
server = cfg.server;
|
|
32
|
-
}
|
|
33
|
-
if (cfg.browser) {
|
|
34
|
-
browser = cfg.browser;
|
|
35
|
-
}
|
|
36
|
-
if (cfg.userId) {
|
|
37
|
-
userId = cfg.userId;
|
|
38
|
-
}
|
|
39
|
-
if (cfg.maxFollowing !== undefined) {
|
|
40
|
-
maxFollowing = cfg.maxFollowing;
|
|
41
|
-
}
|
|
42
|
-
if (cfg.maxFollowers !== undefined) {
|
|
43
|
-
maxFollowers = cfg.maxFollowers;
|
|
44
|
-
}
|
|
45
|
-
if (cfg.maxVideos !== undefined) {
|
|
46
|
-
maxVideos = cfg.maxVideos;
|
|
47
|
-
}
|
|
48
|
-
if (cfg.maxComments !== undefined) {
|
|
49
|
-
maxComments = cfg.maxComments;
|
|
50
|
-
}
|
|
51
|
-
configFile = configPath;
|
|
52
|
-
}
|
|
53
|
-
} catch {
|
|
54
|
-
// no config file
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function saveBrowser(path) {
|
|
58
|
-
const cfg = existsSync(configPath)
|
|
59
|
-
? JSON.parse(readFileSync(configPath, "utf-8"))
|
|
60
|
-
: {};
|
|
61
|
-
cfg.browser = path;
|
|
62
|
-
writeFileSync(configPath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
63
|
-
browser = path;
|
|
64
|
-
configFile = configPath;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function saveUserId(id) {
|
|
68
|
-
const cfg = existsSync(configPath)
|
|
69
|
-
? JSON.parse(readFileSync(configPath, "utf-8"))
|
|
70
|
-
: {};
|
|
71
|
-
cfg.userId = id;
|
|
72
|
-
writeFileSync(configPath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
73
|
-
userId = id;
|
|
74
|
-
configFile = configPath;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function saveMaxFollowing(val) {
|
|
78
|
-
const cfg = existsSync(configPath)
|
|
79
|
-
? JSON.parse(readFileSync(configPath, "utf-8"))
|
|
80
|
-
: {};
|
|
81
|
-
cfg.maxFollowing = parseInt(val) || 5;
|
|
82
|
-
writeFileSync(configPath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
83
|
-
maxFollowing = cfg.maxFollowing;
|
|
84
|
-
configFile = configPath;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function saveMaxFollowers(val) {
|
|
88
|
-
const cfg = existsSync(configPath)
|
|
89
|
-
? JSON.parse(readFileSync(configPath, "utf-8"))
|
|
90
|
-
: {};
|
|
91
|
-
cfg.maxFollowers = parseInt(val) || 5;
|
|
92
|
-
writeFileSync(configPath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
93
|
-
maxFollowers = cfg.maxFollowers;
|
|
94
|
-
configFile = configPath;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function saveMaxVideos(val) {
|
|
98
|
-
const cfg = existsSync(configPath)
|
|
99
|
-
? JSON.parse(readFileSync(configPath, "utf-8"))
|
|
100
|
-
: {};
|
|
101
|
-
cfg.maxVideos = parseInt(val) || 16;
|
|
102
|
-
writeFileSync(configPath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
103
|
-
maxVideos = cfg.maxVideos;
|
|
104
|
-
configFile = configPath;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function saveMaxComments(val) {
|
|
108
|
-
const cfg = existsSync(configPath)
|
|
109
|
-
? JSON.parse(readFileSync(configPath, "utf-8"))
|
|
110
|
-
: {};
|
|
111
|
-
cfg.maxComments = parseInt(val) || 10;
|
|
112
|
-
writeFileSync(configPath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
113
|
-
maxComments = cfg.maxComments;
|
|
114
|
-
configFile = configPath;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const HELP_TEXT = [
|
|
118
|
-
"用法:",
|
|
119
|
-
"",
|
|
120
|
-
" tt-help explore <用户名> [preset] [选项]",
|
|
121
|
-
" 支持多个用户名: tt-help explore @user1 @user2 --server http://127.0.0.1:3001",
|
|
122
|
-
" 预设: fast, normal(默认), slow, stealth",
|
|
123
|
-
" 选项:",
|
|
124
|
-
" --server <URL> 服务端地址,默认 http://127.0.0.1:3001",
|
|
125
|
-
` --location <国家代码> 国家筛选,逗号分隔,默认 ${DEFAULT_TARGET_LOCATIONS_CSV}`,
|
|
126
|
-
" --job-locations <国家> 任务国家筛选,逗号分隔(仅筛选服务端任务)",
|
|
127
|
-
" --max-comments <数量> 每视频最大评论数,默认 10",
|
|
128
|
-
" --max-guess <数量> 每视频最大猜你喜欢数,默认 0",
|
|
129
|
-
" --max-videos <数量> 每用户最大视频数,默认 16",
|
|
130
|
-
" --enable-follow 启用关注/粉丝提取(默认启用)",
|
|
131
|
-
" --disable-follow 禁用关注/粉丝提取",
|
|
132
|
-
" --max-following <数量> 最大获取关注数,默认 50",
|
|
133
|
-
" --max-followers <数量> 最大获取粉丝数,默认 50",
|
|
134
|
-
" --max-users <数量> 最大处理用户数,默认无限制",
|
|
135
|
-
" -i, --interval <秒数> 无任务时轮询间隔,默认 10 秒",
|
|
136
|
-
" --port <端口号> 固定 CDP 端口(调试用,关闭自动轮换)",
|
|
137
|
-
" --base-port <端口号> 起始端口,默认 9222",
|
|
138
|
-
" --port-count <数量> 端口数量(账户数),默认 10",
|
|
139
|
-
" --user-id <编号> 客户端编号(设备ID),默认自动生成",
|
|
140
|
-
"",
|
|
141
|
-
" tt-help info <URL> [URL2 ...] [--onlyvideo]",
|
|
142
|
-
" 获取用户/视频信息,支持多个 URL",
|
|
143
|
-
" 主页 URL → 返回用户信息",
|
|
144
|
-
" 视频 URL → 返回用户信息 + 视频信息",
|
|
145
|
-
" 视频 URL + --onlyvideo → 只返回视频信息",
|
|
146
|
-
" 示例: tt-help info https://www.tiktok.com/@nike",
|
|
147
|
-
" tt-help info https://www.tiktok.com/@nike/video/7234567890 --onlyvideo",
|
|
148
|
-
"",
|
|
149
|
-
" tt-help attach [-p 并行数] [-i 间隔秒数] [-s 服务端地址] [-c 国家列表]",
|
|
150
|
-
" 后台轮询服务端任务接口,自动抓取 TikTok 用户信息",
|
|
151
|
-
" -p, --parallel <N> 并行抓取数(默认: 1)",
|
|
152
|
-
" -i, --interval <N> 无任务时轮询间隔,单位秒(默认: 10)",
|
|
153
|
-
" -s, --server <URL> 服务端地址(默认: http://127.0.0.1:3001)",
|
|
154
|
-
" -c, --countries <A,B> 猜测国家列表(逗号分隔,如 PL,DE,FR)",
|
|
155
|
-
" 示例: tt-help attach -p 5 -i 10",
|
|
156
|
-
" tt-help attach -c PL,DE,FR -p 5",
|
|
157
|
-
"",
|
|
158
|
-
" tt-help watch -o <db路径> [-p 端口]",
|
|
159
|
-
" 启动监控服务;运行期主数据仅来自 SQLite",
|
|
160
|
-
" 示例: tt-help watch -o data/result.db",
|
|
161
|
-
" tt-help watch -o data/result.db -p 8080",
|
|
162
|
-
"",
|
|
163
|
-
" tt-help open <端口号>",
|
|
164
|
-
" 打开指定端口的浏览器,用于配置 TikTok 登录账户",
|
|
165
|
-
" 端口范围: 9222 - 9231(对应 explore 的 10 个内置账户)",
|
|
166
|
-
" --list 列出所有端口和配置",
|
|
167
|
-
" 示例: tt-help open 9222",
|
|
168
|
-
" tt-help open --list",
|
|
169
|
-
"",
|
|
170
|
-
" videostats <db路径> -p <并发数>",
|
|
171
|
-
" 批量刷新视频统计数据(playCount, diggCount, commentCount, shareCount, collectCount)",
|
|
172
|
-
" 直接读取并更新 SQLite videos 表",
|
|
173
|
-
" -p, --parallel <N> 并发数(默认: 3)",
|
|
174
|
-
" 示例: tt-help videostats data/result.db -p 3",
|
|
175
|
-
"",
|
|
176
|
-
" db-import --db <db路径> [--users users.json] [--done done.json] [--videos videos.json]",
|
|
177
|
-
" 将 legacy JSON 显式导入 SQLite;运行期不再自动读取 result*.json",
|
|
178
|
-
" 示例: tt-help db-import --db data/result.db --users data/result.json --done data/result-done.json --videos data/result-videos.json",
|
|
179
|
-
"",
|
|
180
|
-
"
|
|
181
|
-
"
|
|
182
|
-
"
|
|
183
|
-
"
|
|
184
|
-
"",
|
|
185
|
-
"
|
|
186
|
-
"
|
|
187
|
-
"
|
|
188
|
-
"",
|
|
189
|
-
"
|
|
190
|
-
"
|
|
191
|
-
"
|
|
192
|
-
"
|
|
193
|
-
"
|
|
194
|
-
"
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
"
|
|
199
|
-
"
|
|
200
|
-
"
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
" tt-help
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
`
|
|
264
|
-
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
1
|
+
import { join, dirname } from "path";
|
|
2
|
+
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { DEFAULT_TARGET_LOCATIONS_CSV } from "./target-locations.js";
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = dirname(__filename);
|
|
8
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || "";
|
|
9
|
+
const configPath = join(homeDir, ".tt-help.json");
|
|
10
|
+
|
|
11
|
+
const DEFAULT_PROXY = "http://127.0.0.1:7897";
|
|
12
|
+
const DEFAULT_OUTPUT = "tiktok_data.json";
|
|
13
|
+
|
|
14
|
+
let proxy = DEFAULT_PROXY;
|
|
15
|
+
let server = "http://127.0.0.1:3001";
|
|
16
|
+
let configFile = null;
|
|
17
|
+
let browser = null;
|
|
18
|
+
let userId = null;
|
|
19
|
+
let maxFollowing = 50;
|
|
20
|
+
let maxFollowers = 50;
|
|
21
|
+
let maxVideos = 16;
|
|
22
|
+
let maxComments = 10;
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
if (existsSync(configPath)) {
|
|
26
|
+
const cfg = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
27
|
+
if (cfg.proxy) {
|
|
28
|
+
proxy = cfg.proxy;
|
|
29
|
+
}
|
|
30
|
+
if (cfg.server) {
|
|
31
|
+
server = cfg.server;
|
|
32
|
+
}
|
|
33
|
+
if (cfg.browser) {
|
|
34
|
+
browser = cfg.browser;
|
|
35
|
+
}
|
|
36
|
+
if (cfg.userId) {
|
|
37
|
+
userId = cfg.userId;
|
|
38
|
+
}
|
|
39
|
+
if (cfg.maxFollowing !== undefined) {
|
|
40
|
+
maxFollowing = cfg.maxFollowing;
|
|
41
|
+
}
|
|
42
|
+
if (cfg.maxFollowers !== undefined) {
|
|
43
|
+
maxFollowers = cfg.maxFollowers;
|
|
44
|
+
}
|
|
45
|
+
if (cfg.maxVideos !== undefined) {
|
|
46
|
+
maxVideos = cfg.maxVideos;
|
|
47
|
+
}
|
|
48
|
+
if (cfg.maxComments !== undefined) {
|
|
49
|
+
maxComments = cfg.maxComments;
|
|
50
|
+
}
|
|
51
|
+
configFile = configPath;
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// no config file
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function saveBrowser(path) {
|
|
58
|
+
const cfg = existsSync(configPath)
|
|
59
|
+
? JSON.parse(readFileSync(configPath, "utf-8"))
|
|
60
|
+
: {};
|
|
61
|
+
cfg.browser = path;
|
|
62
|
+
writeFileSync(configPath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
63
|
+
browser = path;
|
|
64
|
+
configFile = configPath;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function saveUserId(id) {
|
|
68
|
+
const cfg = existsSync(configPath)
|
|
69
|
+
? JSON.parse(readFileSync(configPath, "utf-8"))
|
|
70
|
+
: {};
|
|
71
|
+
cfg.userId = id;
|
|
72
|
+
writeFileSync(configPath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
73
|
+
userId = id;
|
|
74
|
+
configFile = configPath;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function saveMaxFollowing(val) {
|
|
78
|
+
const cfg = existsSync(configPath)
|
|
79
|
+
? JSON.parse(readFileSync(configPath, "utf-8"))
|
|
80
|
+
: {};
|
|
81
|
+
cfg.maxFollowing = parseInt(val) || 5;
|
|
82
|
+
writeFileSync(configPath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
83
|
+
maxFollowing = cfg.maxFollowing;
|
|
84
|
+
configFile = configPath;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function saveMaxFollowers(val) {
|
|
88
|
+
const cfg = existsSync(configPath)
|
|
89
|
+
? JSON.parse(readFileSync(configPath, "utf-8"))
|
|
90
|
+
: {};
|
|
91
|
+
cfg.maxFollowers = parseInt(val) || 5;
|
|
92
|
+
writeFileSync(configPath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
93
|
+
maxFollowers = cfg.maxFollowers;
|
|
94
|
+
configFile = configPath;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function saveMaxVideos(val) {
|
|
98
|
+
const cfg = existsSync(configPath)
|
|
99
|
+
? JSON.parse(readFileSync(configPath, "utf-8"))
|
|
100
|
+
: {};
|
|
101
|
+
cfg.maxVideos = parseInt(val) || 16;
|
|
102
|
+
writeFileSync(configPath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
103
|
+
maxVideos = cfg.maxVideos;
|
|
104
|
+
configFile = configPath;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function saveMaxComments(val) {
|
|
108
|
+
const cfg = existsSync(configPath)
|
|
109
|
+
? JSON.parse(readFileSync(configPath, "utf-8"))
|
|
110
|
+
: {};
|
|
111
|
+
cfg.maxComments = parseInt(val) || 10;
|
|
112
|
+
writeFileSync(configPath, JSON.stringify(cfg, null, 2), "utf-8");
|
|
113
|
+
maxComments = cfg.maxComments;
|
|
114
|
+
configFile = configPath;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const HELP_TEXT = [
|
|
118
|
+
"用法:",
|
|
119
|
+
"",
|
|
120
|
+
" tt-help explore <用户名> [preset] [选项]",
|
|
121
|
+
" 支持多个用户名: tt-help explore @user1 @user2 --server http://127.0.0.1:3001",
|
|
122
|
+
" 预设: fast, normal(默认), slow, stealth",
|
|
123
|
+
" 选项:",
|
|
124
|
+
" --server <URL> 服务端地址,默认 http://127.0.0.1:3001",
|
|
125
|
+
` --location <国家代码> 国家筛选,逗号分隔,默认 ${DEFAULT_TARGET_LOCATIONS_CSV}`,
|
|
126
|
+
" --job-locations <国家> 任务国家筛选,逗号分隔(仅筛选服务端任务)",
|
|
127
|
+
" --max-comments <数量> 每视频最大评论数,默认 10",
|
|
128
|
+
" --max-guess <数量> 每视频最大猜你喜欢数,默认 0",
|
|
129
|
+
" --max-videos <数量> 每用户最大视频数,默认 16",
|
|
130
|
+
" --enable-follow 启用关注/粉丝提取(默认启用)",
|
|
131
|
+
" --disable-follow 禁用关注/粉丝提取",
|
|
132
|
+
" --max-following <数量> 最大获取关注数,默认 50",
|
|
133
|
+
" --max-followers <数量> 最大获取粉丝数,默认 50",
|
|
134
|
+
" --max-users <数量> 最大处理用户数,默认无限制",
|
|
135
|
+
" -i, --interval <秒数> 无任务时轮询间隔,默认 10 秒",
|
|
136
|
+
" --port <端口号> 固定 CDP 端口(调试用,关闭自动轮换)",
|
|
137
|
+
" --base-port <端口号> 起始端口,默认 9222",
|
|
138
|
+
" --port-count <数量> 端口数量(账户数),默认 10",
|
|
139
|
+
" --user-id <编号> 客户端编号(设备ID),默认自动生成",
|
|
140
|
+
"",
|
|
141
|
+
" tt-help info <URL> [URL2 ...] [--onlyvideo]",
|
|
142
|
+
" 获取用户/视频信息,支持多个 URL",
|
|
143
|
+
" 主页 URL → 返回用户信息",
|
|
144
|
+
" 视频 URL → 返回用户信息 + 视频信息",
|
|
145
|
+
" 视频 URL + --onlyvideo → 只返回视频信息",
|
|
146
|
+
" 示例: tt-help info https://www.tiktok.com/@nike",
|
|
147
|
+
" tt-help info https://www.tiktok.com/@nike/video/7234567890 --onlyvideo",
|
|
148
|
+
"",
|
|
149
|
+
" tt-help attach [-p 并行数] [-i 间隔秒数] [-s 服务端地址] [-c 国家列表]",
|
|
150
|
+
" 后台轮询服务端任务接口,自动抓取 TikTok 用户信息",
|
|
151
|
+
" -p, --parallel <N> 并行抓取数(默认: 1)",
|
|
152
|
+
" -i, --interval <N> 无任务时轮询间隔,单位秒(默认: 10)",
|
|
153
|
+
" -s, --server <URL> 服务端地址(默认: http://127.0.0.1:3001)",
|
|
154
|
+
" -c, --countries <A,B> 猜测国家列表(逗号分隔,如 PL,DE,FR)",
|
|
155
|
+
" 示例: tt-help attach -p 5 -i 10",
|
|
156
|
+
" tt-help attach -c PL,DE,FR -p 5",
|
|
157
|
+
"",
|
|
158
|
+
" tt-help watch -o <db路径> [-p 端口]",
|
|
159
|
+
" 启动监控服务;运行期主数据仅来自 SQLite",
|
|
160
|
+
" 示例: tt-help watch -o data/result.db",
|
|
161
|
+
" tt-help watch -o data/result.db -p 8080",
|
|
162
|
+
"",
|
|
163
|
+
" tt-help open <端口号>",
|
|
164
|
+
" 打开指定端口的浏览器,用于配置 TikTok 登录账户",
|
|
165
|
+
" 端口范围: 9222 - 9231(对应 explore 的 10 个内置账户)",
|
|
166
|
+
" --list 列出所有端口和配置",
|
|
167
|
+
" 示例: tt-help open 9222",
|
|
168
|
+
" tt-help open --list",
|
|
169
|
+
"",
|
|
170
|
+
" videostats <db路径> -p <并发数>",
|
|
171
|
+
" 批量刷新视频统计数据(playCount, diggCount, commentCount, shareCount, collectCount)",
|
|
172
|
+
" 直接读取并更新 SQLite videos 表",
|
|
173
|
+
" -p, --parallel <N> 并发数(默认: 3)",
|
|
174
|
+
" 示例: tt-help videostats data/result.db -p 3",
|
|
175
|
+
"",
|
|
176
|
+
" db-import --db <db路径> [--users users.json] [--done done.json] [--videos videos.json]",
|
|
177
|
+
" 将 legacy JSON 显式导入 SQLite;运行期不再自动读取 result*.json",
|
|
178
|
+
" 示例: tt-help db-import --db data/result.db --users data/result.json --done data/result-done.json --videos data/result-videos.json",
|
|
179
|
+
"",
|
|
180
|
+
" webserver [选项]",
|
|
181
|
+
" 启动 HTTP 服务,对外提供 TikTok 数据抓取 API",
|
|
182
|
+
" 选项:",
|
|
183
|
+
" -p, --port <端口号> 监听端口,默认 3000",
|
|
184
|
+
" API:",
|
|
185
|
+
" POST /api/tiktok/user 获取用户信息 { uniqueId: string }",
|
|
186
|
+
" POST /api/tiktok/video 获取视频信息 { videoUrl: string }",
|
|
187
|
+
" POST /api/tiktok/lookup 同时获取视频和作者信息 { videoUrl: string }",
|
|
188
|
+
" 示例: tt-help webserver -p 3000",
|
|
189
|
+
"",
|
|
190
|
+
" config [show|set|reset]",
|
|
191
|
+
" config 查看当前配置",
|
|
192
|
+
" config set <key> <value> 设置配置(key: proxy, server, browser, userId, maxFollowing, maxFollowers, maxVideos, maxComments)",
|
|
193
|
+
" config reset 重置所有配置为默认",
|
|
194
|
+
"",
|
|
195
|
+
" 全局选项:",
|
|
196
|
+
" -h, --help 显示帮助",
|
|
197
|
+
" --version 显示版本号",
|
|
198
|
+
"",
|
|
199
|
+
" 示例: tt-help info https://www.tiktok.com/@nike https://www.tiktok.com/@adidas",
|
|
200
|
+
" tt-help explore qiqi23280 fast --location ES --max-comments 50",
|
|
201
|
+
" tt-help config set server http://127.0.0.1:3001",
|
|
202
|
+
" tt-help attach -p 5 -i 10",
|
|
203
|
+
" tt-help watch -o data/result.db",
|
|
204
|
+
" tt-help videostats data/result.db -p 3",
|
|
205
|
+
];
|
|
206
|
+
|
|
207
|
+
const PUBLIC_HELP_HIDDEN_HEADERS = new Set([
|
|
208
|
+
" tt-help watch -o <db路径> [-p 端口]",
|
|
209
|
+
" videostats <db路径> -p <并发数>",
|
|
210
|
+
" db-import --db <db路径> [--users users.json] [--done done.json] [--videos videos.json]",
|
|
211
|
+
]);
|
|
212
|
+
|
|
213
|
+
const PUBLIC_HELP_HIDDEN_LINES = new Set([
|
|
214
|
+
" tt-help watch -o data/result.db",
|
|
215
|
+
" tt-help videostats data/result.db -p 3",
|
|
216
|
+
]);
|
|
217
|
+
|
|
218
|
+
function removeHelpSections(lines, hiddenHeaders, hiddenLines = new Set()) {
|
|
219
|
+
const result = [];
|
|
220
|
+
let skipping = false;
|
|
221
|
+
|
|
222
|
+
for (const line of lines) {
|
|
223
|
+
if (hiddenHeaders.has(line)) {
|
|
224
|
+
skipping = true;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (skipping) {
|
|
229
|
+
if (line === "") {
|
|
230
|
+
skipping = false;
|
|
231
|
+
}
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (hiddenLines.has(line)) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
result.push(line);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return result;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const PUBLIC_HELP_TEXT = removeHelpSections(
|
|
246
|
+
HELP_TEXT,
|
|
247
|
+
PUBLIC_HELP_HIDDEN_HEADERS,
|
|
248
|
+
PUBLIC_HELP_HIDDEN_LINES,
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
function getConfigText() {
|
|
252
|
+
let currentUserId = userId;
|
|
253
|
+
if (!currentUserId && existsSync(configPath)) {
|
|
254
|
+
try {
|
|
255
|
+
const cfg = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
256
|
+
if (cfg.userId) currentUserId = cfg.userId;
|
|
257
|
+
} catch {}
|
|
258
|
+
}
|
|
259
|
+
return [
|
|
260
|
+
"tt-help v1.0.1",
|
|
261
|
+
"",
|
|
262
|
+
"配置:",
|
|
263
|
+
` 代理: ${proxy}`,
|
|
264
|
+
` 服务端: ${server}`,
|
|
265
|
+
` 浏览器: ${browser || "未配置(将自动探测或回退)"}`,
|
|
266
|
+
` 用户号: ${currentUserId || "未设置(首次运行 auto 自动创建)"}`,
|
|
267
|
+
` 商家关注采集数: ${maxFollowing}`,
|
|
268
|
+
` 粉丝采集数: ${maxFollowers}`,
|
|
269
|
+
` 视频采集数: ${maxVideos}`,
|
|
270
|
+
` 评论采集数: ${maxComments}`,
|
|
271
|
+
` 输出格式: json`,
|
|
272
|
+
` 默认输出: ${DEFAULT_OUTPUT}`,
|
|
273
|
+
` 配置文件: ${configFile || "无(使用默认值)"}`,
|
|
274
|
+
];
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export {
|
|
278
|
+
proxy,
|
|
279
|
+
server,
|
|
280
|
+
configPath,
|
|
281
|
+
DEFAULT_PROXY,
|
|
282
|
+
HELP_TEXT,
|
|
283
|
+
PUBLIC_HELP_TEXT,
|
|
284
|
+
browser,
|
|
285
|
+
userId,
|
|
286
|
+
maxFollowing,
|
|
287
|
+
maxFollowers,
|
|
288
|
+
maxVideos,
|
|
289
|
+
maxComments,
|
|
290
|
+
saveBrowser,
|
|
291
|
+
saveUserId,
|
|
292
|
+
saveMaxFollowing,
|
|
293
|
+
saveMaxFollowers,
|
|
294
|
+
saveMaxVideos,
|
|
295
|
+
saveMaxComments,
|
|
296
|
+
getConfigText,
|
|
297
|
+
};
|