tt-help-cli-ycl 1.3.48 → 1.3.49

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 +111 -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 +91 -40
  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";
@@ -10,7 +9,7 @@ const DEFAULT_WAF_TTL = 120000;
10
9
  const DEFAULT_WARM_URL = "https://www.tiktok.com/@nike";
11
10
  const BROWSER_CLOSE_TIMEOUT = 5000;
12
11
  const DEFAULT_MAX_REQUESTS_PER_PAGE = 50;
13
- const FALLBACK_PROFILE_PORT = 9999;
12
+ const FALLBACK_PROFILE_PORT = 9222;
14
13
 
15
14
  function delay(ms) {
16
15
  return new Promise((r) => setTimeout(r, ms));
@@ -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]));
@@ -136,7 +133,11 @@ export class TikTokScraper {
136
133
  "未找到本地浏览器(Chrome/Edge),请先安装浏览器或执行 npx playwright install",
137
134
  );
138
135
  }
139
- const { browser, context, slots } = await initContext(executablePath, this.poolSize, null);
136
+ const { browser, context, slots } = await initContext(
137
+ executablePath,
138
+ this.poolSize,
139
+ null,
140
+ );
140
141
  this.browser = browser;
141
142
  this.context = context;
142
143
  this.slots = slots;
@@ -145,16 +146,18 @@ export class TikTokScraper {
145
146
 
146
147
  async _ensureFallback(executablePath) {
147
148
  if (this._fallbackContext) return;
148
- if (!fallbackProfileExists()) {
149
- return false;
150
- }
151
149
  const fallbackDir = getFallbackProfileDir();
152
- console.error(`[TikTokScraper] 无登录态获取失败,fallback 到登录态 profile: ${fallbackDir}`);
153
- const { browser, context, slots } = await initContext(executablePath, 1, fallbackDir);
150
+ console.error(
151
+ `[TikTokScraper] 无登录态获取失败,fallback 到登录态 profile: ${fallbackDir}`,
152
+ );
153
+ const { browser, context, slots } = await initContext(
154
+ executablePath,
155
+ 1,
156
+ fallbackDir,
157
+ );
154
158
  this._fallbackBrowser = browser;
155
159
  this._fallbackContext = context;
156
160
  this._fallbackSlots = slots;
157
- return true;
158
161
  }
159
162
 
160
163
  async close() {
@@ -168,7 +171,9 @@ export class TikTokScraper {
168
171
  });
169
172
  await Promise.race([
170
173
  closePromise,
171
- delay(BROWSER_CLOSE_TIMEOUT).then(() => { closeTimedOut = true; }),
174
+ delay(BROWSER_CLOSE_TIMEOUT).then(() => {
175
+ closeTimedOut = true;
176
+ }),
172
177
  ]);
173
178
  if (closeTimedOut) {
174
179
  console.error(
@@ -182,7 +187,9 @@ export class TikTokScraper {
182
187
  await closeAll(this.browser);
183
188
  // fallback 的 context 是 launchPersistentContext 返回的,直接 close
184
189
  if (this._fallbackContext) {
185
- try { await this._fallbackContext.close(); } catch {}
190
+ try {
191
+ await this._fallbackContext.close();
192
+ } catch {}
186
193
  }
187
194
  this.browser = null;
188
195
  this.context = null;
@@ -237,7 +244,10 @@ export class TikTokScraper {
237
244
 
238
245
  async _ensurePage(slot, context) {
239
246
  try {
240
- if (!slot.page.isClosed() && slot.requestCount < this.maxRequestsPerPage) {
247
+ if (
248
+ !slot.page.isClosed() &&
249
+ slot.requestCount < this.maxRequestsPerPage
250
+ ) {
241
251
  return slot.page;
242
252
  }
243
253
  } catch {}
@@ -248,23 +258,51 @@ export class TikTokScraper {
248
258
  }
249
259
 
250
260
  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++;
261
+ const slots =
262
+ ctx === this._fallbackContext ? this._fallbackSlots : this.slots;
263
+ const slotIdx =
264
+ ctx === this._fallbackContext ? this._fallbackSlotIdx : this.slotIdx;
265
+ const slot = slots[slotIdx % slots.length];
266
+ if (ctx === this._fallbackContext) {
267
+ this._fallbackSlotIdx++;
268
+ } else {
269
+ this.slotIdx++;
270
+ }
254
271
 
255
272
  try {
256
273
  return await slot.lock.run(async () => {
257
274
  const page = await this._ensurePage(slot, ctx);
258
- slot.requestCount++;
275
+
259
276
  if (ctx === this.context && this.isWarmExpired()) {
260
277
  await this.warmWaf();
261
278
  }
262
- await page.goto(url, {
279
+
280
+ await page.goto("view-source:" + url, {
263
281
  waitUntil: "domcontentloaded",
264
282
  timeout: 15000,
265
283
  });
266
- await delay(1500);
267
- return await page.content();
284
+
285
+ const content = await page.evaluate(() => {
286
+ const rows = document.querySelectorAll("tr");
287
+ let content = "";
288
+ rows.forEach((r) => {
289
+ const lc = r.querySelector(".line-content");
290
+ if (lc) content += lc.textContent + "\n";
291
+ });
292
+ return content;
293
+ });
294
+
295
+ // 导航到 about:blank 释放当前页面的 DOM 和 JS 堆
296
+ await page
297
+ .goto("about:blank", {
298
+ waitUntil: "domcontentloaded",
299
+ timeout: 5000,
300
+ })
301
+ .catch(() => {});
302
+
303
+ slot.requestCount += 1;
304
+
305
+ return content;
268
306
  });
269
307
  } catch (e) {
270
308
  console.error(`[TikTokScraper] _fetchViewSource failed: ${e.message}`);
@@ -277,38 +315,51 @@ export class TikTokScraper {
277
315
  ? username.slice(1)
278
316
  : username;
279
317
  const url = `https://www.tiktok.com/@${normalizedUsername}`;
280
- const executablePath = detectBrowser();
281
318
 
282
319
  // 第一趟:无登录态
283
320
  let rawHtml = await this._fetchViewSource(url, this.context);
284
321
  let result = rawHtml ? parseUserInfo(rawHtml) : null;
285
322
 
286
- // 解析失败:fallback 到登录态
323
+ // 解析失败:先尝试 warmWaf 后重试
287
324
  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
- }
325
+ try {
326
+ await this.warmWaf();
327
+ } catch {}
328
+ rawHtml = await this._fetchViewSource(url, this.context);
329
+ result = rawHtml ? parseUserInfo(rawHtml) : null;
330
+ }
331
+
332
+ // 仍然失败:fallback 到登录态
333
+ if (!result) {
334
+ const executablePath = detectBrowser();
335
+ await this._ensureFallback(executablePath);
336
+ rawHtml = await this._fetchViewSource(url, this._fallbackContext);
337
+ result = rawHtml ? parseUserInfo(rawHtml) : null;
293
338
  }
294
339
 
295
340
  return result || null;
296
341
  }
297
342
 
298
343
  async getVideoInfo(videoUrl) {
299
- const executablePath = detectBrowser();
300
-
301
344
  // 第一趟:无登录态
302
345
  let rawHtml = await this._fetchViewSource(videoUrl, this.context);
303
346
  let result = rawHtml ? parseVideoInfo(rawHtml) : null;
304
347
 
305
- // 解析失败:fallback 到登录态
348
+ // 解析失败:先尝试 warmWaf 后重试
306
349
  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
- }
350
+ try {
351
+ await this.warmWaf();
352
+ } catch {}
353
+ rawHtml = await this._fetchViewSource(videoUrl, this.context);
354
+ result = rawHtml ? parseVideoInfo(rawHtml) : null;
355
+ }
356
+
357
+ // 仍然失败:fallback 到登录态
358
+ if (!result) {
359
+ const executablePath = detectBrowser();
360
+ await this._ensureFallback(executablePath);
361
+ rawHtml = await this._fetchViewSource(videoUrl, this._fallbackContext);
362
+ result = rawHtml ? parseVideoInfo(rawHtml) : null;
312
363
  }
313
364
 
314
365
  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
+ });
@@ -0,0 +1,37 @@
1
+ {
2
+ "user": {
3
+ "uniqueId": "bar.lar.lar.moeta",
4
+ "secUid": "MS4wLjABAAAA3cgKTWvKfga0JAWeakAzx3zQ-aFAC8RuQvxD4HQFraKKsc_TbOIyMo3_ofVlXofV",
5
+ "nickname": "Bar Lar Lar Moetain",
6
+ "ttSeller": false,
7
+ "verified": false,
8
+ "followerCount": 24000,
9
+ "videoCount": 749,
10
+ "followingCount": 4293,
11
+ "heartCount": 254300,
12
+ "signature": ""
13
+ },
14
+ "totalVideos": 5,
15
+ "videos": [
16
+ {
17
+ "id": "7638231799084158228",
18
+ "url": "https://www.tiktok.com/@bar.lar.lar.moeta/video/7638231799084158228"
19
+ },
20
+ {
21
+ "id": "7638162444698914068",
22
+ "url": "https://www.tiktok.com/@bar.lar.lar.moeta/video/7638162444698914068"
23
+ },
24
+ {
25
+ "id": "7638116251767819541",
26
+ "url": "https://www.tiktok.com/@bar.lar.lar.moeta/video/7638116251767819541"
27
+ },
28
+ {
29
+ "id": "7638069637321690388",
30
+ "url": "https://www.tiktok.com/@bar.lar.lar.moeta/video/7638069637321690388"
31
+ },
32
+ {
33
+ "id": "7637927171025112341",
34
+ "url": "https://www.tiktok.com/@bar.lar.lar.moeta/video/7637927171025112341"
35
+ }
36
+ ]
37
+ }