tt-help-cli-ycl 1.3.48 → 1.3.50

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.
Files changed (64) hide show
  1. package/README.md +33 -33
  2. package/cli.js +9 -9
  3. package/package.json +52 -52
  4. package/scripts/run-explore copy.bat +101 -101
  5. package/scripts/run-explore.bat +134 -134
  6. package/scripts/run-explore.ps1 +159 -159
  7. package/scripts/run-explore.sh +121 -121
  8. package/scripts/test-captcha-lib.mjs +68 -0
  9. package/scripts/test-captcha.mjs +81 -0
  10. package/scripts/test-incognito-lib.mjs +36 -0
  11. package/scripts/test-login-state.mjs +128 -0
  12. package/scripts/test-safe-click.mjs +45 -0
  13. package/scripts/test-watch-db-smoke.mjs +246 -0
  14. package/src/cli/attach.js +331 -331
  15. package/src/cli/auto.js +265 -265
  16. package/src/cli/comments.js +620 -620
  17. package/src/cli/config.js +170 -170
  18. package/src/cli/db-import.js +51 -51
  19. package/src/cli/explore.js +555 -555
  20. package/src/cli/open.js +109 -111
  21. package/src/cli/progress.js +111 -111
  22. package/src/cli/refresh.js +288 -288
  23. package/src/cli/scrape.js +47 -47
  24. package/src/cli/utils.js +18 -18
  25. package/src/cli/videos.js +41 -41
  26. package/src/cli/videostats.js +196 -196
  27. package/src/cli/watch.js +30 -30
  28. package/src/lib/api-interceptor.js +161 -161
  29. package/src/lib/args.js +809 -809
  30. package/src/lib/browser/anti-detect.js +23 -23
  31. package/src/lib/browser/cdp.js +261 -261
  32. package/src/lib/browser/health-checker.js +114 -114
  33. package/src/lib/browser/launch.js +43 -43
  34. package/src/lib/browser/page.js +184 -184
  35. package/src/lib/constants.js +297 -297
  36. package/src/lib/delay.js +54 -54
  37. package/src/lib/explore-fetch.js +118 -118
  38. package/src/lib/fetcher.js +45 -45
  39. package/src/lib/filter.js +66 -66
  40. package/src/lib/io.js +54 -54
  41. package/src/lib/output.js +80 -80
  42. package/src/lib/page-error-detector.js +109 -109
  43. package/src/lib/parse-ssr.mjs +69 -69
  44. package/src/lib/parser.js +47 -47
  45. package/src/lib/retry.js +45 -45
  46. package/src/lib/scrape.js +90 -90
  47. package/src/lib/target-locations.js +61 -61
  48. package/src/lib/tiktok-scraper.mjs +98 -61
  49. package/src/lib/url.js +52 -52
  50. package/src/main.js +73 -73
  51. package/src/npm-main.js +70 -70
  52. package/src/results/user-videos-bar.lar.lar.moeta.json +37 -0
  53. package/src/scraper/auto-core.js +203 -203
  54. package/src/scraper/core.js +255 -255
  55. package/src/scraper/explore-core.js +208 -208
  56. package/src/scraper/modules/captcha-handler.js +114 -114
  57. package/src/scraper/modules/follow-extractor.js +250 -250
  58. package/src/scraper/modules/guess-extractor.js +51 -51
  59. package/src/scraper/modules/page-helpers.js +48 -48
  60. package/src/scraper/refresh-core.js +213 -213
  61. package/src/videos/core.js +143 -143
  62. package/src/watch/data-store.js +2980 -2980
  63. package/src/watch/public/index.html +2355 -2355
  64. package/src/watch/server.js +727 -727
@@ -1,4 +1,3 @@
1
- import fs from "fs";
2
1
  import os from "os";
3
2
  import path from "path";
4
3
  import { chromium } from "playwright";
@@ -26,11 +25,6 @@ function getFallbackProfileDir() {
26
25
  );
27
26
  }
28
27
 
29
- function fallbackProfileExists() {
30
- const dir = getFallbackProfileDir();
31
- return fs.existsSync(dir);
32
- }
33
-
34
28
  class PageSlot {
35
29
  constructor(page) {
36
30
  this.page = page;
@@ -87,7 +81,10 @@ async function initContext(executablePath, poolSize, userDataDir) {
87
81
  const slots = [];
88
82
 
89
83
  if (userDataDir) {
90
- context = await chromium.launchPersistentContext(userDataDir, createLaunchOptions(executablePath));
84
+ context = await chromium.launchPersistentContext(
85
+ userDataDir,
86
+ createLaunchOptions(executablePath),
87
+ );
91
88
  const existing = context.pages();
92
89
  if (existing.length > 0) {
93
90
  slots.push(new PageSlot(existing[0]));
@@ -123,10 +120,11 @@ export class TikTokScraper {
123
120
  this.slotIdx = 0;
124
121
  this.lastWarmTime = 0;
125
122
  this.warmPromise = null;
126
- this._fallbackBrowser = null;
127
- this._fallbackContext = null;
128
- this._fallbackSlots = [];
129
- this._fallbackSlotIdx = 0;
123
+ // 登录态 pool(init 时直接启动)
124
+ this.authBrowser = null;
125
+ this.authContext = null;
126
+ this.authSlots = [];
127
+ this.authSlotIdx = 0;
130
128
  }
131
129
 
132
130
  async init() {
@@ -136,25 +134,27 @@ export class TikTokScraper {
136
134
  "未找到本地浏览器(Chrome/Edge),请先安装浏览器或执行 npx playwright install",
137
135
  );
138
136
  }
139
- const { browser, context, slots } = await initContext(executablePath, this.poolSize, null);
137
+ const { browser, context, slots } = await initContext(
138
+ executablePath,
139
+ this.poolSize,
140
+ null,
141
+ );
140
142
  this.browser = browser;
141
143
  this.context = context;
142
144
  this.slots = slots;
143
- await this.warmWaf();
144
- }
145
145
 
146
- async _ensureFallback(executablePath) {
147
- if (this._fallbackContext) return;
148
- if (!fallbackProfileExists()) {
149
- return false;
150
- }
146
+ // 启动登录态 pool(1 个 slot)
151
147
  const fallbackDir = getFallbackProfileDir();
152
- console.error(`[TikTokScraper] 无登录态获取失败,fallback 到登录态 profile: ${fallbackDir}`);
153
- const { browser, context, slots } = await initContext(executablePath, 1, fallbackDir);
154
- this._fallbackBrowser = browser;
155
- this._fallbackContext = context;
156
- this._fallbackSlots = slots;
157
- return true;
148
+ const {
149
+ browser: authBrowser,
150
+ context: authContext,
151
+ slots: authSlots,
152
+ } = await initContext(executablePath, 1, fallbackDir);
153
+ this.authBrowser = authBrowser;
154
+ this.authContext = authContext;
155
+ this.authSlots = authSlots;
156
+
157
+ await this.warmWaf();
158
158
  }
159
159
 
160
160
  async close() {
@@ -168,7 +168,9 @@ export class TikTokScraper {
168
168
  });
169
169
  await Promise.race([
170
170
  closePromise,
171
- delay(BROWSER_CLOSE_TIMEOUT).then(() => { closeTimedOut = true; }),
171
+ delay(BROWSER_CLOSE_TIMEOUT).then(() => {
172
+ closeTimedOut = true;
173
+ }),
172
174
  ]);
173
175
  if (closeTimedOut) {
174
176
  console.error(
@@ -178,18 +180,20 @@ export class TikTokScraper {
178
180
  }
179
181
  };
180
182
 
181
- // 无登录态的 browser 是 chromium.launch() 返回的,直接 close
183
+ // 无登录态的 browser
182
184
  await closeAll(this.browser);
183
- // fallback context launchPersistentContext 返回的,直接 close
184
- if (this._fallbackContext) {
185
- try { await this._fallbackContext.close(); } catch {}
185
+ // 登录态的 context(launchPersistentContext 返回的是 context browser)
186
+ if (this.authContext) {
187
+ try {
188
+ await this.authContext.close();
189
+ } catch {}
186
190
  }
187
191
  this.browser = null;
188
192
  this.context = null;
189
193
  this.slots = [];
190
- this._fallbackBrowser = null;
191
- this._fallbackContext = null;
192
- this._fallbackSlots = [];
194
+ this.authBrowser = null;
195
+ this.authContext = null;
196
+ this.authSlots = [];
193
197
  }
194
198
 
195
199
  async restart() {
@@ -230,14 +234,12 @@ export class TikTokScraper {
230
234
  return Date.now() - this.lastWarmTime > this.wafTtl;
231
235
  }
232
236
 
233
- _pickSlot(slots, idx, setIdx) {
234
- const slot = slots[idx % slots.length];
235
- return slot;
236
- }
237
-
238
237
  async _ensurePage(slot, context) {
239
238
  try {
240
- if (!slot.page.isClosed() && slot.requestCount < this.maxRequestsPerPage) {
239
+ if (
240
+ !slot.page.isClosed() &&
241
+ slot.requestCount < this.maxRequestsPerPage
242
+ ) {
241
243
  return slot.page;
242
244
  }
243
245
  } catch {}
@@ -248,23 +250,49 @@ export class TikTokScraper {
248
250
  }
249
251
 
250
252
  async _fetchViewSource(url, ctx) {
251
- const slots = ctx === this._fallbackContext ? this._fallbackSlots : this.slots;
252
- const slot = slots[this.slotIdx % slots.length];
253
- this.slotIdx++;
253
+ const slots = ctx === this.authContext ? this.authSlots : this.slots;
254
+ const slotIdx = ctx === this.authContext ? this.authSlotIdx : this.slotIdx;
255
+ const slot = slots[slotIdx % slots.length];
256
+ if (ctx === this.authContext) {
257
+ this.authSlotIdx++;
258
+ } else {
259
+ this.slotIdx++;
260
+ }
254
261
 
255
262
  try {
256
263
  return await slot.lock.run(async () => {
257
264
  const page = await this._ensurePage(slot, ctx);
258
- slot.requestCount++;
265
+
259
266
  if (ctx === this.context && this.isWarmExpired()) {
260
267
  await this.warmWaf();
261
268
  }
262
- await page.goto(url, {
269
+
270
+ await page.goto("view-source:" + url, {
263
271
  waitUntil: "domcontentloaded",
264
272
  timeout: 15000,
265
273
  });
266
- await delay(1500);
267
- return await page.content();
274
+
275
+ const content = await page.evaluate(() => {
276
+ const rows = document.querySelectorAll("tr");
277
+ let content = "";
278
+ rows.forEach((r) => {
279
+ const lc = r.querySelector(".line-content");
280
+ if (lc) content += lc.textContent + "\n";
281
+ });
282
+ return content;
283
+ });
284
+
285
+ // 导航到 about:blank 释放当前页面的 DOM 和 JS 堆
286
+ await page
287
+ .goto("about:blank", {
288
+ waitUntil: "domcontentloaded",
289
+ timeout: 5000,
290
+ })
291
+ .catch(() => {});
292
+
293
+ slot.requestCount += 1;
294
+
295
+ return content;
268
296
  });
269
297
  } catch (e) {
270
298
  console.error(`[TikTokScraper] _fetchViewSource failed: ${e.message}`);
@@ -277,38 +305,47 @@ export class TikTokScraper {
277
305
  ? username.slice(1)
278
306
  : username;
279
307
  const url = `https://www.tiktok.com/@${normalizedUsername}`;
280
- const executablePath = detectBrowser();
281
308
 
282
309
  // 第一趟:无登录态
283
310
  let rawHtml = await this._fetchViewSource(url, this.context);
284
311
  let result = rawHtml ? parseUserInfo(rawHtml) : null;
285
312
 
286
- // 解析失败:fallback 到登录态
313
+ // 解析失败:先尝试 warmWaf 后重试
287
314
  if (!result) {
288
- const ok = await this._ensureFallback(executablePath);
289
- if (ok) {
290
- rawHtml = await this._fetchViewSource(url, this._fallbackContext);
291
- result = rawHtml ? parseUserInfo(rawHtml) : null;
292
- }
315
+ try {
316
+ await this.warmWaf();
317
+ } catch {}
318
+ rawHtml = await this._fetchViewSource(url, this.context);
319
+ result = rawHtml ? parseUserInfo(rawHtml) : null;
320
+ }
321
+
322
+ // 仍然失败:使用登录态 pool
323
+ if (!result) {
324
+ rawHtml = await this._fetchViewSource(url, this.authContext);
325
+ result = rawHtml ? parseUserInfo(rawHtml) : null;
293
326
  }
294
327
 
295
328
  return result || null;
296
329
  }
297
330
 
298
331
  async getVideoInfo(videoUrl) {
299
- const executablePath = detectBrowser();
300
-
301
332
  // 第一趟:无登录态
302
333
  let rawHtml = await this._fetchViewSource(videoUrl, this.context);
303
334
  let result = rawHtml ? parseVideoInfo(rawHtml) : null;
304
335
 
305
- // 解析失败:fallback 到登录态
336
+ // 解析失败:先尝试 warmWaf 后重试
306
337
  if (!result) {
307
- const ok = await this._ensureFallback(executablePath);
308
- if (ok) {
309
- rawHtml = await this._fetchViewSource(videoUrl, this._fallbackContext);
310
- result = rawHtml ? parseVideoInfo(rawHtml) : null;
311
- }
338
+ try {
339
+ await this.warmWaf();
340
+ } catch {}
341
+ rawHtml = await this._fetchViewSource(videoUrl, this.context);
342
+ result = rawHtml ? parseVideoInfo(rawHtml) : null;
343
+ }
344
+
345
+ // 仍然失败:使用登录态 pool
346
+ if (!result) {
347
+ rawHtml = await this._fetchViewSource(videoUrl, this.authContext);
348
+ result = rawHtml ? parseVideoInfo(rawHtml) : null;
312
349
  }
313
350
 
314
351
  return result || null;
package/src/lib/url.js CHANGED
@@ -1,52 +1,52 @@
1
- const BASE_URL = 'https://www.tiktok.com';
2
-
3
- export function extractUniqueId(url) {
4
- const m = url.match(/\/@([\w.-]+)/);
5
- return m ? m[1] : null;
6
- }
7
-
8
- export function extractVideoId(url) {
9
- const m = url.match(/\/video\/(\d+)/);
10
- return m ? m[1] : null;
11
- }
12
-
13
- export function normalizeUsername(input) {
14
- return (input || '').replace(/^@/, '');
15
- }
16
-
17
- export function toProfileUrl(handle) {
18
- const clean = normalizeUsername(handle);
19
- return `${BASE_URL}/@${clean}`;
20
- }
21
-
22
- export function toVideoUrl(handle, videoId) {
23
- const clean = normalizeUsername(handle);
24
- return `${BASE_URL}/@${clean}/video/${videoId}`;
25
- }
26
-
27
- export function ensureAbsoluteUrl(href) {
28
- if (href.startsWith('http')) return href;
29
- return `${BASE_URL}${href}`;
30
- }
31
-
32
- export function isProfileUrl(url) {
33
- return /\/@[\w.-]+(?:$|[?#])/.test(url);
34
- }
35
-
36
- export function isVideoUrl(url) {
37
- return /\/video\/\d+/.test(url);
38
- }
39
-
40
- export function extractDisplayPath(url) {
41
- try {
42
- const parts = new URL(url).pathname.split('/').filter(Boolean);
43
- return parts.slice(-2).join('/');
44
- } catch {
45
- return url;
46
- }
47
- }
48
-
49
- export function extractAuthorFromVideoUrl(url) {
50
- const m = url.match(/@([^/]+)\/video/);
51
- return m ? '@' + m[1] : null;
52
- }
1
+ const BASE_URL = 'https://www.tiktok.com';
2
+
3
+ export function extractUniqueId(url) {
4
+ const m = url.match(/\/@([\w.-]+)/);
5
+ return m ? m[1] : null;
6
+ }
7
+
8
+ export function extractVideoId(url) {
9
+ const m = url.match(/\/video\/(\d+)/);
10
+ return m ? m[1] : null;
11
+ }
12
+
13
+ export function normalizeUsername(input) {
14
+ return (input || '').replace(/^@/, '');
15
+ }
16
+
17
+ export function toProfileUrl(handle) {
18
+ const clean = normalizeUsername(handle);
19
+ return `${BASE_URL}/@${clean}`;
20
+ }
21
+
22
+ export function toVideoUrl(handle, videoId) {
23
+ const clean = normalizeUsername(handle);
24
+ return `${BASE_URL}/@${clean}/video/${videoId}`;
25
+ }
26
+
27
+ export function ensureAbsoluteUrl(href) {
28
+ if (href.startsWith('http')) return href;
29
+ return `${BASE_URL}${href}`;
30
+ }
31
+
32
+ export function isProfileUrl(url) {
33
+ return /\/@[\w.-]+(?:$|[?#])/.test(url);
34
+ }
35
+
36
+ export function isVideoUrl(url) {
37
+ return /\/video\/\d+/.test(url);
38
+ }
39
+
40
+ export function extractDisplayPath(url) {
41
+ try {
42
+ const parts = new URL(url).pathname.split('/').filter(Boolean);
43
+ return parts.slice(-2).join('/');
44
+ } catch {
45
+ return url;
46
+ }
47
+ }
48
+
49
+ export function extractAuthorFromVideoUrl(url) {
50
+ const m = url.match(/@([^/]+)\/video/);
51
+ return m ? '@' + m[1] : null;
52
+ }
package/src/main.js CHANGED
@@ -1,73 +1,73 @@
1
- import { parseArgs } from "./lib/args.js";
2
- import { proxy, HELP_TEXT, getConfigText } from "./lib/constants.js";
3
- import { handleInfo } from "./cli/info.js";
4
- import { handleExplore } from "./cli/explore.js";
5
- import { handleAttach } from "./cli/attach.js";
6
- import { handleWatch } from "./cli/watch.js";
7
- import { handleConfig, showConfig, showUsage, version } from "./cli/config.js";
8
- import { handleOpen } from "./cli/open.js";
9
- import { handleComments } from "./cli/comments.js";
10
- import { handleVideoStats } from "./cli/videostats.js";
11
- import { handleDbImport } from "./cli/db-import.js";
12
- import { handleWebserver } from "./cli/webserver.js";
13
-
14
- async function main() {
15
- const parsed = parseArgs();
16
-
17
- switch (parsed.subcommand) {
18
- case "explore":
19
- return handleExplore(parsed);
20
- case "info":
21
- return handleInfo(parsed);
22
- case "attach":
23
- return handleAttach(parsed);
24
- case "watch":
25
- return handleWatch(parsed);
26
- case "webserver":
27
- return handleWebserver(parsed);
28
- case "open":
29
- return handleOpen(parsed);
30
- case "comments":
31
- return handleComments(parsed);
32
- case "videostats":
33
- return handleVideoStats(parsed);
34
- case "db-import":
35
- return handleDbImport(parsed);
36
- }
37
-
38
- const {
39
- urls,
40
- outputFile,
41
- outputFormat,
42
- exploreCount,
43
- showConfig: showCfg,
44
- showHelp,
45
- showVersion,
46
- customProxy,
47
- configAction,
48
- configKey,
49
- configValue,
50
- } = parsed;
51
-
52
- if (showVersion) {
53
- console.log(version);
54
- process.exit(0);
55
- }
56
- if (showHelp) return showUsage();
57
- if (configAction) return handleConfig(configAction, configKey, configValue);
58
- if (showCfg) return showConfig(urls, outputFile);
59
- if (urls.length === 0 && exploreCount === 0) return showUsage();
60
-
61
- // 默认行为:URL 走 info,--explore 走 explore
62
- if (exploreCount > 0) {
63
- return handleExplore({ ...parsed, subcommand: "explore" });
64
- }
65
-
66
- // 有 URL 默认走 info
67
- return handleInfo(parsed);
68
- }
69
-
70
- main().catch((err) => {
71
- console.error(`错误: ${err.message}`);
72
- process.exit(1);
73
- });
1
+ import { parseArgs } from "./lib/args.js";
2
+ import { proxy, HELP_TEXT, getConfigText } from "./lib/constants.js";
3
+ import { handleInfo } from "./cli/info.js";
4
+ import { handleExplore } from "./cli/explore.js";
5
+ import { handleAttach } from "./cli/attach.js";
6
+ import { handleWatch } from "./cli/watch.js";
7
+ import { handleConfig, showConfig, showUsage, version } from "./cli/config.js";
8
+ import { handleOpen } from "./cli/open.js";
9
+ import { handleComments } from "./cli/comments.js";
10
+ import { handleVideoStats } from "./cli/videostats.js";
11
+ import { handleDbImport } from "./cli/db-import.js";
12
+ import { handleWebserver } from "./cli/webserver.js";
13
+
14
+ async function main() {
15
+ const parsed = parseArgs();
16
+
17
+ switch (parsed.subcommand) {
18
+ case "explore":
19
+ return handleExplore(parsed);
20
+ case "info":
21
+ return handleInfo(parsed);
22
+ case "attach":
23
+ return handleAttach(parsed);
24
+ case "watch":
25
+ return handleWatch(parsed);
26
+ case "webserver":
27
+ return handleWebserver(parsed);
28
+ case "open":
29
+ return handleOpen(parsed);
30
+ case "comments":
31
+ return handleComments(parsed);
32
+ case "videostats":
33
+ return handleVideoStats(parsed);
34
+ case "db-import":
35
+ return handleDbImport(parsed);
36
+ }
37
+
38
+ const {
39
+ urls,
40
+ outputFile,
41
+ outputFormat,
42
+ exploreCount,
43
+ showConfig: showCfg,
44
+ showHelp,
45
+ showVersion,
46
+ customProxy,
47
+ configAction,
48
+ configKey,
49
+ configValue,
50
+ } = parsed;
51
+
52
+ if (showVersion) {
53
+ console.log(version);
54
+ process.exit(0);
55
+ }
56
+ if (showHelp) return showUsage();
57
+ if (configAction) return handleConfig(configAction, configKey, configValue);
58
+ if (showCfg) return showConfig(urls, outputFile);
59
+ if (urls.length === 0 && exploreCount === 0) return showUsage();
60
+
61
+ // 默认行为:URL 走 info,--explore 走 explore
62
+ if (exploreCount > 0) {
63
+ return handleExplore({ ...parsed, subcommand: "explore" });
64
+ }
65
+
66
+ // 有 URL 默认走 info
67
+ return handleInfo(parsed);
68
+ }
69
+
70
+ main().catch((err) => {
71
+ console.error(`错误: ${err.message}`);
72
+ process.exit(1);
73
+ });
package/src/npm-main.js CHANGED
@@ -1,70 +1,70 @@
1
- import { parseArgs } from "./lib/args.js";
2
- import { PUBLIC_HELP_TEXT } from "./lib/constants.js";
3
- import { handleInfo } from "./cli/info.js";
4
- import { handleExplore } from "./cli/explore.js";
5
- import { handleAttach } from "./cli/attach.js";
6
- import { handleConfig, showConfig, showUsage, version } from "./cli/config.js";
7
- import { handleOpen } from "./cli/open.js";
8
- import { handleComments } from "./cli/comments.js";
9
-
10
- function exitUnsupportedCommand(command) {
11
- console.error(
12
- `[${command}] 当前 npm 发布包不包含该命令;如需使用,请在仓库源码环境中运行 node src/main.js ${command} ...`,
13
- );
14
- process.exit(1);
15
- }
16
-
17
- async function main() {
18
- const parsed = parseArgs();
19
-
20
- switch (parsed.subcommand) {
21
- case "explore":
22
- return handleExplore(parsed);
23
- case "info":
24
- return handleInfo(parsed);
25
- case "attach":
26
- return handleAttach(parsed);
27
- case "watch":
28
- case "webserver":
29
- case "videostats":
30
- case "db-import":
31
- return exitUnsupportedCommand(parsed.subcommand);
32
- case "open":
33
- return handleOpen(parsed);
34
- case "comments":
35
- return handleComments(parsed);
36
- }
37
-
38
- const {
39
- urls,
40
- outputFile,
41
- exploreCount,
42
- showConfig: showCfg,
43
- showHelp,
44
- showVersion,
45
- configAction,
46
- configKey,
47
- configValue,
48
- } = parsed;
49
-
50
- if (showVersion) {
51
- console.log(version);
52
- process.exit(0);
53
- }
54
- if (showHelp) return showUsage(PUBLIC_HELP_TEXT);
55
- if (configAction) return handleConfig(configAction, configKey, configValue);
56
- if (showCfg) return showConfig(urls, outputFile);
57
- if (urls.length === 0 && exploreCount === 0)
58
- return showUsage(PUBLIC_HELP_TEXT);
59
-
60
- if (exploreCount > 0) {
61
- return handleExplore({ ...parsed, subcommand: "explore" });
62
- }
63
-
64
- return handleInfo(parsed);
65
- }
66
-
67
- main().catch((err) => {
68
- console.error(`错误: ${err.message}`);
69
- process.exit(1);
70
- });
1
+ import { parseArgs } from "./lib/args.js";
2
+ import { PUBLIC_HELP_TEXT } from "./lib/constants.js";
3
+ import { handleInfo } from "./cli/info.js";
4
+ import { handleExplore } from "./cli/explore.js";
5
+ import { handleAttach } from "./cli/attach.js";
6
+ import { handleConfig, showConfig, showUsage, version } from "./cli/config.js";
7
+ import { handleOpen } from "./cli/open.js";
8
+ import { handleComments } from "./cli/comments.js";
9
+
10
+ function exitUnsupportedCommand(command) {
11
+ console.error(
12
+ `[${command}] 当前 npm 发布包不包含该命令;如需使用,请在仓库源码环境中运行 node src/main.js ${command} ...`,
13
+ );
14
+ process.exit(1);
15
+ }
16
+
17
+ async function main() {
18
+ const parsed = parseArgs();
19
+
20
+ switch (parsed.subcommand) {
21
+ case "explore":
22
+ return handleExplore(parsed);
23
+ case "info":
24
+ return handleInfo(parsed);
25
+ case "attach":
26
+ return handleAttach(parsed);
27
+ case "watch":
28
+ case "webserver":
29
+ case "videostats":
30
+ case "db-import":
31
+ return exitUnsupportedCommand(parsed.subcommand);
32
+ case "open":
33
+ return handleOpen(parsed);
34
+ case "comments":
35
+ return handleComments(parsed);
36
+ }
37
+
38
+ const {
39
+ urls,
40
+ outputFile,
41
+ exploreCount,
42
+ showConfig: showCfg,
43
+ showHelp,
44
+ showVersion,
45
+ configAction,
46
+ configKey,
47
+ configValue,
48
+ } = parsed;
49
+
50
+ if (showVersion) {
51
+ console.log(version);
52
+ process.exit(0);
53
+ }
54
+ if (showHelp) return showUsage(PUBLIC_HELP_TEXT);
55
+ if (configAction) return handleConfig(configAction, configKey, configValue);
56
+ if (showCfg) return showConfig(urls, outputFile);
57
+ if (urls.length === 0 && exploreCount === 0)
58
+ return showUsage(PUBLIC_HELP_TEXT);
59
+
60
+ if (exploreCount > 0) {
61
+ return handleExplore({ ...parsed, subcommand: "explore" });
62
+ }
63
+
64
+ return handleInfo(parsed);
65
+ }
66
+
67
+ main().catch((err) => {
68
+ console.error(`错误: ${err.message}`);
69
+ process.exit(1);
70
+ });