tt-help-cli-ycl 1.3.45 → 1.3.47

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 (67) 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/src/cli/attach.js +331 -313
  9. package/src/cli/auto.js +265 -265
  10. package/src/cli/comments.js +620 -620
  11. package/src/cli/config.js +170 -170
  12. package/src/cli/db-import.js +51 -51
  13. package/src/cli/explore.js +555 -555
  14. package/src/cli/info.js +10 -16
  15. package/src/cli/open.js +111 -111
  16. package/src/cli/progress.js +111 -111
  17. package/src/cli/refresh.js +288 -288
  18. package/src/cli/scrape.js +47 -47
  19. package/src/cli/utils.js +18 -18
  20. package/src/cli/videos.js +41 -41
  21. package/src/cli/videostats.js +196 -196
  22. package/src/cli/watch.js +30 -30
  23. package/src/cli/webserver.js +19 -0
  24. package/src/lib/api-interceptor.js +161 -161
  25. package/src/lib/args.js +809 -778
  26. package/src/lib/browser/anti-detect.js +23 -23
  27. package/src/lib/browser/cdp.js +261 -261
  28. package/src/lib/browser/health-checker.js +114 -114
  29. package/src/lib/browser/launch.js +43 -43
  30. package/src/lib/browser/page.js +184 -184
  31. package/src/lib/constants.js +297 -287
  32. package/src/lib/delay.js +54 -54
  33. package/src/lib/explore-fetch.js +118 -118
  34. package/src/lib/fetcher.js +45 -45
  35. package/src/lib/filter.js +66 -66
  36. package/src/lib/io.js +54 -54
  37. package/src/lib/output.js +80 -80
  38. package/src/lib/page-error-detector.js +109 -109
  39. package/src/lib/parse-ssr.mjs +69 -69
  40. package/src/lib/parser.js +47 -47
  41. package/src/lib/retry.js +45 -45
  42. package/src/lib/scrape.js +90 -89
  43. package/src/lib/target-locations.js +61 -61
  44. package/src/lib/tiktok-scraper.mjs +173 -105
  45. package/src/lib/url.js +52 -52
  46. package/src/main.js +73 -70
  47. package/src/npm-main.js +70 -69
  48. package/src/scraper/auto-core.js +203 -203
  49. package/src/scraper/core.js +255 -255
  50. package/src/scraper/explore-core.js +208 -208
  51. package/src/scraper/modules/captcha-handler.js +114 -114
  52. package/src/scraper/modules/follow-extractor.js +250 -250
  53. package/src/scraper/modules/guess-extractor.js +51 -51
  54. package/src/scraper/modules/page-helpers.js +48 -48
  55. package/src/scraper/refresh-core.js +213 -213
  56. package/src/videos/core.js +143 -143
  57. package/src/watch/data-store.js +2980 -2978
  58. package/src/watch/public/index.html +2355 -2345
  59. package/src/watch/server.js +727 -727
  60. package/src/webserver/server.mjs +174 -0
  61. package/scripts/test-captcha-lib.mjs +0 -68
  62. package/scripts/test-captcha.mjs +0 -81
  63. package/scripts/test-incognito-lib.mjs +0 -36
  64. package/scripts/test-login-state.mjs +0 -128
  65. package/scripts/test-safe-click.mjs +0 -45
  66. package/scripts/test-watch-db-smoke.mjs +0 -246
  67. package/src/results/user-videos-bar.lar.lar.moeta.json +0 -37
package/src/cli/info.js CHANGED
@@ -34,25 +34,21 @@ async function handleInfo(options) {
34
34
  const uniqueId = extractUniqueId(url);
35
35
  const normalized = normalizeUsername(uniqueId);
36
36
  const user = await scraper.getUserInfo(normalized);
37
- if (!user) {
38
- console.error(`无法获取用户 @${uniqueId} 的信息`);
39
- continue;
37
+ if (user) {
38
+ result[normalized] = { user };
39
+ console.error(`用户: @${user.uniqueId} (${user.nickname})`);
40
40
  }
41
- result[normalized] = { user };
42
- console.error(`用户: @${user.uniqueId} (${user.nickname})`);
43
41
  } else if (isVideoUrl(url)) {
44
42
  const uniqueId = extractUniqueId(url);
45
43
  const normalized = normalizeUsername(uniqueId);
46
44
 
47
45
  if (infoOnlyVideo) {
48
46
  const video = await scraper.getVideoInfo(url);
49
- if (!video) {
50
- console.error(`无法获取视频信息: ${url}`);
51
- continue;
47
+ if (video) {
48
+ const key = normalized + '/video/' + video.id;
49
+ result[key] = { video };
50
+ console.error(`视频: ${video.id}`);
52
51
  }
53
- const key = normalized + '/video/' + video.id;
54
- result[key] = { video };
55
- console.error(`视频: ${video.id}`);
56
52
  } else {
57
53
  const [user, video] = await Promise.all([
58
54
  scraper.getUserInfo(normalized),
@@ -67,12 +63,10 @@ async function handleInfo(options) {
67
63
  entry.video = video;
68
64
  console.error(`视频: ${video.id}`);
69
65
  }
70
- if (!user && !video) {
71
- console.error(`无法获取信息: ${url}`);
72
- continue;
66
+ if (user || video) {
67
+ const key = normalized + '/video/' + (video ? video.id : 'unknown');
68
+ result[key] = entry;
73
69
  }
74
- const key = normalized + '/video/' + (video ? video.id : 'unknown');
75
- result[key] = entry;
76
70
  }
77
71
  } else {
78
72
  console.error(`无法识别 URL: ${url}`);
package/src/cli/open.js CHANGED
@@ -1,111 +1,111 @@
1
- import { ensureBrowserReady, killEdgeProcesses } from "../lib/browser/cdp.js";
2
- import { getOrCreatePage } from "../lib/browser/page.js";
3
- import path from "path";
4
- import os from "os";
5
-
6
- const BASE_PORT = 9222;
7
- const TOTAL_ACCOUNTS = 10;
8
-
9
- export async function handleOpen(parsed) {
10
- const { openPort, openList } = parsed;
11
-
12
- if (openList) {
13
- console.error("内置浏览器配置:");
14
- for (let i = 0; i < TOTAL_ACCOUNTS; i++) {
15
- const port = BASE_PORT + i;
16
- const profile = `p${port}`;
17
- const userDataDir = path.join(
18
- os.homedir(),
19
- "Library",
20
- "Application Support",
21
- `Microsoft Edge For Testing_${profile}`,
22
- );
23
- console.error(
24
- ` open ${port} → profile: ${profile}, userDataDir: ${userDataDir}`,
25
- );
26
- }
27
- console.error("");
28
- console.error("用法: tt-help open <端口>");
29
- console.error("示例: tt-help open 9222");
30
- process.exit(0);
31
- }
32
-
33
- if (!openPort) {
34
- console.error("用法: tt-help open <端口>");
35
- console.error("示例: tt-help open 9222");
36
- console.error("");
37
- console.error("可用端口: 9222 - 9231 (共 10 个)");
38
- console.error('运行 "tt-help open --list" 查看所有配置');
39
- process.exit(1);
40
- }
41
-
42
- const port = parseInt(openPort);
43
- if (isNaN(port) || port < BASE_PORT || port >= BASE_PORT + TOTAL_ACCOUNTS) {
44
- console.error(`端口 ${openPort} 不在有效范围内 (9222 - 9231)`);
45
- process.exit(1);
46
- }
47
-
48
- const profile = `p${port}`;
49
- const userDataDir = path.join(
50
- os.homedir(),
51
- "Library",
52
- "Application Support",
53
- `Microsoft Edge For Testing_${profile}`,
54
- );
55
-
56
- console.error(`正在启动浏览器... 端口: ${port}, profile: ${profile}`);
57
- console.error(`userDataDir: ${userDataDir}`);
58
- console.error("");
59
- console.error("启动后请在浏览器中登录 TikTok 账户");
60
- console.error("登录完成后关闭浏览器即可,下次 open 会保留登录状态");
61
- console.error("");
62
- console.error("按 Ctrl+C 退出并关闭浏览器");
63
-
64
- let browser = null;
65
-
66
- try {
67
- const cdpOptions = {};
68
- cdpOptions.port = port;
69
- cdpOptions.userDataDir = userDataDir;
70
-
71
- browser = await ensureBrowserReady(cdpOptions);
72
-
73
- // Ctrl+C 关闭浏览器
74
- process.on("SIGINT", async () => {
75
- console.error("\n正在关闭浏览器...");
76
- try {
77
- await killEdgeProcesses(userDataDir);
78
- await browser.close();
79
- } catch {
80
- /* ignore */
81
- }
82
- console.error("浏览器已关闭");
83
- process.exit(0);
84
- });
85
-
86
- console.error("");
87
- console.error("浏览器已启动并连接成功");
88
- console.error("TikTok 页面将在浏览器中自动打开...");
89
-
90
- // 打开 TikTok 首页
91
- try {
92
- const page = await getOrCreatePage(browser);
93
- if (page) {
94
- await page.goto("https://www.tiktok.com", {
95
- waitUntil: "domcontentloaded",
96
- timeout: 30000,
97
- });
98
- console.error("已打开 https://www.tiktok.com");
99
- }
100
- } catch (e) {
101
- console.error(`打开页面时出错: ${e.message}`);
102
- console.error("浏览器已启动,可以手动在浏览器中打开 TikTok");
103
- }
104
-
105
- // 保持进程运行
106
- await new Promise(() => {});
107
- } catch (err) {
108
- console.error(`启动失败: ${err.message}`);
109
- process.exit(1);
110
- }
111
- }
1
+ import { ensureBrowserReady, killEdgeProcesses } from "../lib/browser/cdp.js";
2
+ import { getOrCreatePage } from "../lib/browser/page.js";
3
+ import path from "path";
4
+ import os from "os";
5
+
6
+ const BASE_PORT = 9222;
7
+ const TOTAL_ACCOUNTS = 10;
8
+
9
+ export async function handleOpen(parsed) {
10
+ const { openPort, openList } = parsed;
11
+
12
+ if (openList) {
13
+ console.error("内置浏览器配置:");
14
+ for (let i = 0; i < TOTAL_ACCOUNTS; i++) {
15
+ const port = BASE_PORT + i;
16
+ const profile = `p${port}`;
17
+ const userDataDir = path.join(
18
+ os.homedir(),
19
+ "Library",
20
+ "Application Support",
21
+ `Microsoft Edge For Testing_${profile}`,
22
+ );
23
+ console.error(
24
+ ` open ${port} → profile: ${profile}, userDataDir: ${userDataDir}`,
25
+ );
26
+ }
27
+ console.error("");
28
+ console.error("用法: tt-help open <端口>");
29
+ console.error("示例: tt-help open 9222");
30
+ process.exit(0);
31
+ }
32
+
33
+ if (!openPort) {
34
+ console.error("用法: tt-help open <端口>");
35
+ console.error("示例: tt-help open 9222");
36
+ console.error("");
37
+ console.error("可用端口: 9222 - 9231 (共 10 个)");
38
+ console.error('运行 "tt-help open --list" 查看所有配置');
39
+ process.exit(1);
40
+ }
41
+
42
+ const port = parseInt(openPort);
43
+ if (isNaN(port) || port < BASE_PORT || port >= BASE_PORT + TOTAL_ACCOUNTS) {
44
+ console.error(`端口 ${openPort} 不在有效范围内 (9222 - 9231)`);
45
+ process.exit(1);
46
+ }
47
+
48
+ const profile = `p${port}`;
49
+ const userDataDir = path.join(
50
+ os.homedir(),
51
+ "Library",
52
+ "Application Support",
53
+ `Microsoft Edge For Testing_${profile}`,
54
+ );
55
+
56
+ console.error(`正在启动浏览器... 端口: ${port}, profile: ${profile}`);
57
+ console.error(`userDataDir: ${userDataDir}`);
58
+ console.error("");
59
+ console.error("启动后请在浏览器中登录 TikTok 账户");
60
+ console.error("登录完成后关闭浏览器即可,下次 open 会保留登录状态");
61
+ console.error("");
62
+ console.error("按 Ctrl+C 退出并关闭浏览器");
63
+
64
+ let browser = null;
65
+
66
+ try {
67
+ const cdpOptions = {};
68
+ cdpOptions.port = port;
69
+ cdpOptions.userDataDir = userDataDir;
70
+
71
+ browser = await ensureBrowserReady(cdpOptions);
72
+
73
+ // Ctrl+C 关闭浏览器
74
+ process.on("SIGINT", async () => {
75
+ console.error("\n正在关闭浏览器...");
76
+ try {
77
+ await killEdgeProcesses(userDataDir);
78
+ await browser.close();
79
+ } catch {
80
+ /* ignore */
81
+ }
82
+ console.error("浏览器已关闭");
83
+ process.exit(0);
84
+ });
85
+
86
+ console.error("");
87
+ console.error("浏览器已启动并连接成功");
88
+ console.error("TikTok 页面将在浏览器中自动打开...");
89
+
90
+ // 打开 TikTok 首页
91
+ try {
92
+ const page = await getOrCreatePage(browser);
93
+ if (page) {
94
+ await page.goto("https://www.tiktok.com", {
95
+ waitUntil: "domcontentloaded",
96
+ timeout: 30000,
97
+ });
98
+ console.error("已打开 https://www.tiktok.com");
99
+ }
100
+ } catch (e) {
101
+ console.error(`打开页面时出错: ${e.message}`);
102
+ console.error("浏览器已启动,可以手动在浏览器中打开 TikTok");
103
+ }
104
+
105
+ // 保持进程运行
106
+ await new Promise(() => {});
107
+ } catch (err) {
108
+ console.error(`启动失败: ${err.message}`);
109
+ process.exit(1);
110
+ }
111
+ }
@@ -1,111 +1,111 @@
1
- import { writeFileSync } from 'fs';
2
- import { formatOutput } from '../lib/output.js';
3
- import { deduplicate } from '../lib/output.js';
4
- import { applyFilter, formatFilterDescription } from '../lib/filter.js';
5
- import { calculateConcurrency, createMultiProgressBars, renderMultiProgressBars, clearProgressBars } from '../lib/io.js';
6
- import { randomDelay } from '../lib/delay.js';
7
-
8
- export async function processUrlsWithProgress({
9
- urls,
10
- proxyUrl,
11
- outputFile,
12
- outputFormat,
13
- filter,
14
- processFn,
15
- label = '数据',
16
- }) {
17
- const allResults = [];
18
- const errors = [];
19
-
20
- if (urls.length === 0) {
21
- console.error('\n未获取到数据');
22
- if (outputFile) writeFileSync(outputFile, '[]', 'utf-8');
23
- return;
24
- }
25
-
26
- const concurrency = calculateConcurrency(urls.length);
27
- const bars = createMultiProgressBars(concurrency);
28
-
29
- const slots = Array.from({ length: concurrency }, () => []);
30
- urls.forEach((url, i) => slots[i % concurrency].push(url));
31
-
32
- bars.forEach((bar, i) => {
33
- bar.total = slots[i].length;
34
- bar.status = slots[i].length > 0 ? 'running' : 'done';
35
- });
36
-
37
- renderMultiProgressBars(bars);
38
-
39
- const workers = slots.map(async (slotUrls, slotIndex) => {
40
- for (const url of slotUrls) {
41
- bars[slotIndex].url = url;
42
- renderMultiProgressBars(bars);
43
-
44
- await randomDelay();
45
-
46
- try {
47
- const results = await processFn(url, proxyUrl);
48
- allResults.push(...results);
49
- bars[slotIndex].current++;
50
- bars[slotIndex].status = 'running';
51
- } catch (err) {
52
- errors.push({ url, message: err.message });
53
- bars[slotIndex].current++;
54
- bars[slotIndex].status = 'error';
55
- }
56
-
57
- renderMultiProgressBars(bars);
58
- }
59
- bars[slotIndex].status = bars[slotIndex].current === bars[slotIndex].total ? 'done' : 'error';
60
- renderMultiProgressBars(bars);
61
- });
62
-
63
- await Promise.all(workers);
64
- clearProgressBars();
65
-
66
- const uniqueResults = deduplicate(allResults);
67
- const filteredResults = applyFilter(uniqueResults, filter);
68
-
69
- if (errors.length > 0) {
70
- const firstMsg = errors[0].message;
71
- const isProxyError = ['不可用', '连接被拒绝', '连接中断', '超时', '无法解析']
72
- .some(kw => firstMsg.includes(kw));
73
-
74
- if (filteredResults.length === 0) {
75
- if (isProxyError) {
76
- console.error(` 所有请求失败,请检查代理: ${proxyUrl}\n`);
77
- } else {
78
- const show = errors.slice(0, 5);
79
- for (const e of show) console.error(` ✗ ${e.url}: ${e.message}\n`);
80
- if (errors.length > 5) console.error(` ... 还有 ${errors.length - 5} 个失败\n`);
81
- }
82
- console.error('未获取到数据');
83
- if (outputFile) writeFileSync(outputFile, '[]', 'utf-8');
84
- return;
85
- } else {
86
- if (isProxyError) {
87
- console.error(` ${errors.length} 个请求失败,请检查代理: ${proxyUrl}\n`);
88
- } else {
89
- console.error(` ${errors.length} 个失败:`);
90
- const show = errors.slice(0, 5);
91
- for (const e of show) console.error(` ✗ ${e.url}: ${e.message}`);
92
- if (errors.length > 5) console.error(` ... 还有 ${errors.length - 5} 个`);
93
- }
94
- }
95
- }
96
-
97
- const output = formatOutput(filteredResults, outputFormat);
98
-
99
- if (outputFile) {
100
- writeFileSync(outputFile, output, 'utf-8');
101
- console.log(`\n结果已写入: ${outputFile}`);
102
- } else {
103
- process.stdout.write(output + '\n');
104
- }
105
-
106
- if (filter) {
107
- console.log(`\n共 ${uniqueResults.length} 个${label},过滤后 ${filteredResults.length} 个(过滤条件: ${formatFilterDescription(filter)})`);
108
- } else {
109
- console.log(`\n共 ${filteredResults.length} 个${label}`);
110
- }
111
- }
1
+ import { writeFileSync } from 'fs';
2
+ import { formatOutput } from '../lib/output.js';
3
+ import { deduplicate } from '../lib/output.js';
4
+ import { applyFilter, formatFilterDescription } from '../lib/filter.js';
5
+ import { calculateConcurrency, createMultiProgressBars, renderMultiProgressBars, clearProgressBars } from '../lib/io.js';
6
+ import { randomDelay } from '../lib/delay.js';
7
+
8
+ export async function processUrlsWithProgress({
9
+ urls,
10
+ proxyUrl,
11
+ outputFile,
12
+ outputFormat,
13
+ filter,
14
+ processFn,
15
+ label = '数据',
16
+ }) {
17
+ const allResults = [];
18
+ const errors = [];
19
+
20
+ if (urls.length === 0) {
21
+ console.error('\n未获取到数据');
22
+ if (outputFile) writeFileSync(outputFile, '[]', 'utf-8');
23
+ return;
24
+ }
25
+
26
+ const concurrency = calculateConcurrency(urls.length);
27
+ const bars = createMultiProgressBars(concurrency);
28
+
29
+ const slots = Array.from({ length: concurrency }, () => []);
30
+ urls.forEach((url, i) => slots[i % concurrency].push(url));
31
+
32
+ bars.forEach((bar, i) => {
33
+ bar.total = slots[i].length;
34
+ bar.status = slots[i].length > 0 ? 'running' : 'done';
35
+ });
36
+
37
+ renderMultiProgressBars(bars);
38
+
39
+ const workers = slots.map(async (slotUrls, slotIndex) => {
40
+ for (const url of slotUrls) {
41
+ bars[slotIndex].url = url;
42
+ renderMultiProgressBars(bars);
43
+
44
+ await randomDelay();
45
+
46
+ try {
47
+ const results = await processFn(url, proxyUrl);
48
+ allResults.push(...results);
49
+ bars[slotIndex].current++;
50
+ bars[slotIndex].status = 'running';
51
+ } catch (err) {
52
+ errors.push({ url, message: err.message });
53
+ bars[slotIndex].current++;
54
+ bars[slotIndex].status = 'error';
55
+ }
56
+
57
+ renderMultiProgressBars(bars);
58
+ }
59
+ bars[slotIndex].status = bars[slotIndex].current === bars[slotIndex].total ? 'done' : 'error';
60
+ renderMultiProgressBars(bars);
61
+ });
62
+
63
+ await Promise.all(workers);
64
+ clearProgressBars();
65
+
66
+ const uniqueResults = deduplicate(allResults);
67
+ const filteredResults = applyFilter(uniqueResults, filter);
68
+
69
+ if (errors.length > 0) {
70
+ const firstMsg = errors[0].message;
71
+ const isProxyError = ['不可用', '连接被拒绝', '连接中断', '超时', '无法解析']
72
+ .some(kw => firstMsg.includes(kw));
73
+
74
+ if (filteredResults.length === 0) {
75
+ if (isProxyError) {
76
+ console.error(` 所有请求失败,请检查代理: ${proxyUrl}\n`);
77
+ } else {
78
+ const show = errors.slice(0, 5);
79
+ for (const e of show) console.error(` ✗ ${e.url}: ${e.message}\n`);
80
+ if (errors.length > 5) console.error(` ... 还有 ${errors.length - 5} 个失败\n`);
81
+ }
82
+ console.error('未获取到数据');
83
+ if (outputFile) writeFileSync(outputFile, '[]', 'utf-8');
84
+ return;
85
+ } else {
86
+ if (isProxyError) {
87
+ console.error(` ${errors.length} 个请求失败,请检查代理: ${proxyUrl}\n`);
88
+ } else {
89
+ console.error(` ${errors.length} 个失败:`);
90
+ const show = errors.slice(0, 5);
91
+ for (const e of show) console.error(` ✗ ${e.url}: ${e.message}`);
92
+ if (errors.length > 5) console.error(` ... 还有 ${errors.length - 5} 个`);
93
+ }
94
+ }
95
+ }
96
+
97
+ const output = formatOutput(filteredResults, outputFormat);
98
+
99
+ if (outputFile) {
100
+ writeFileSync(outputFile, output, 'utf-8');
101
+ console.log(`\n结果已写入: ${outputFile}`);
102
+ } else {
103
+ process.stdout.write(output + '\n');
104
+ }
105
+
106
+ if (filter) {
107
+ console.log(`\n共 ${uniqueResults.length} 个${label},过滤后 ${filteredResults.length} 个(过滤条件: ${formatFilterDescription(filter)})`);
108
+ } else {
109
+ console.log(`\n共 ${filteredResults.length} 个${label}`);
110
+ }
111
+ }