tt-help-cli-ycl 1.0.2 → 1.0.4

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/src/main.mjs CHANGED
@@ -1,245 +1,280 @@
1
- import { parseArgs } from './lib/args.js';
2
- import { HELP_TEXT, CONFIG_TEXT, proxy, configFile, configPath, DEFAULT_PROXY } from './lib/constants.js';
3
- import { fetchExplore } from './lib/explore.js';
4
- import { processUrl } from './lib/scrape.js';
5
- import { deduplicate, formatOutput } from './lib/output.js';
6
- import { writeFileSync, readFileSync, existsSync } from 'fs';
7
-
8
- function showConfig(urls, outputFile) {
9
- const lines = [...CONFIG_TEXT];
10
- if (outputFile) lines.push(` 输出文件: ${outputFile}`);
11
- if (urls.length > 0) lines.push(` 待处理URL: ${urls.length}`);
12
- lines.push('', '参数:', ' -c, --config 显示当前配置', ' -h, --help 显示帮助');
13
- console.log(lines.join('\n'));
14
- }
15
-
16
- function showUsage() {
17
- console.log(HELP_TEXT.join('\n'));
18
- process.exit(0);
19
- }
20
-
21
- function handleConfig(action, value) {
22
- if (action === 'show' || action === null) {
23
- showConfig([], null);
24
- return;
25
- }
26
- if (action === 'set' || action === 'set-proxy') {
27
- if (!value) {
28
- console.error('用法: tt-help config set <代理地址>');
29
- process.exit(1);
30
- }
31
- const cfg = { proxy: value };
32
- writeFileSync(configPath, JSON.stringify(cfg, null, 2), 'utf-8');
33
- console.log(`代理已设置为: ${value}`);
34
- console.log(`配置文件: ${configPath}`);
35
- return;
36
- }
37
- if (action === 'reset') {
38
- if (existsSync(configPath)) {
39
- readFileSync(configPath, 'utf-8');
40
- writeFileSync(configPath, JSON.stringify({ proxy: DEFAULT_PROXY }, null, 2), 'utf-8');
41
- console.log(`已恢复默认代理: ${DEFAULT_PROXY}`);
42
- console.log(`配置文件: ${configPath}`);
43
- } else {
44
- console.log('当前使用默认代理,无需重置');
45
- }
46
- return;
47
- }
48
- console.error(`未知配置命令: ${action}`);
49
- console.error('用法: tt-help config [show|set|reset]');
50
- process.exit(1);
51
- }
52
-
53
- function cleanError(msg) {
54
- return msg
55
- .replace(/\x1b\[[0-9;]*m/g, '')
56
- .replace(/\s*- navigating to.*/s, '')
57
- .replace(/\s*Call log:/s, '')
58
- .trim();
59
- }
60
-
61
- async function runExplore(exploreCount, urls, proxyUrl, outputFile, outputFormat, isPipe) {
62
- console.log(`\n代理: ${proxyUrl}`);
63
- console.log(`Explore 数量: ${exploreCount}`);
64
- if (urls.length > 0) {
65
- console.log(`额外 URL: ${urls.length}\n`);
66
- } else {
67
- console.log('');
68
- }
69
-
70
- const allResults = [];
71
-
72
- if (exploreCount > 0) {
73
- try {
74
- const exploreResults = await fetchExplore(exploreCount);
75
- console.log(` 获取到 ${exploreResults.length} 个视频\n`);
76
- if (isPipe) {
77
- const videoUrls = exploreResults.map(r => r.url).filter(Boolean);
78
- if (videoUrls.length > 0) {
79
- await runScrape(videoUrls, proxyUrl, outputFile, outputFormat);
80
- return;
81
- }
82
- }
83
- allResults.push(...exploreResults);
84
- } catch (err) {
85
- console.error(` Explore 获取失败: ${cleanError(err.message)}\n`);
86
- console.error(` 请确保代理 ${proxyUrl} 正常运行\n`);
87
- }
88
- }
89
-
90
- if (urls.length > 0) {
91
- const errors = [];
92
-
93
- for (let i = 0; i < urls.length; i++) {
94
- const bar = '█'.repeat(i + 1).padEnd(urls.length);
95
- process.stdout.write(`\r [${bar}] ${i + 1}/${urls.length}`);
96
- try {
97
- const results = await processUrl(urls[i], proxyUrl);
98
- allResults.push(...results);
99
- } catch (err) {
100
- errors.push({ url: urls[i], message: err.message });
101
- }
102
- }
103
- console.log();
104
-
105
- if (errors.length > 0) {
106
- const msg = errors[0].message;
107
- if (msg.includes('不可用') || msg.includes('连接被拒绝') || msg.includes('连接中断') ||
108
- msg.includes('超时') || msg.includes('无法解析')) {
109
- console.error(` ${errors.length} 个请求失败,请检查代理是否可用: ${proxyUrl}\n`);
110
- } else {
111
- console.error(` ${errors.length} 个失败:`);
112
- const show = errors.slice(0, 5);
113
- for (const e of show) {
114
- console.error(` ✗ ${e.url}: ${e.message}`);
115
- }
116
- if (errors.length > 5) {
117
- console.error(` ... 还有 ${errors.length - 5} 个`);
118
- }
119
- }
120
- }
121
- }
122
-
123
- const uniqueResults = deduplicate(allResults);
124
-
125
- if (uniqueResults.length === 0) {
126
- console.log('\n未获取到数据');
127
- if (outputFile) {
128
- writeFileSync(outputFile, '[]', 'utf-8');
129
- }
130
- return;
131
- }
132
-
133
- const output = formatOutput(uniqueResults, outputFormat);
134
-
135
- if (outputFile) {
136
- writeFileSync(outputFile, output, 'utf-8');
137
- console.log(`\n结果已写入: ${outputFile}`);
138
- } else {
139
- console.log(output);
140
- }
141
- console.log(`\n共 ${uniqueResults.length} 个数据`);
142
- }
143
-
144
- async function runScrape(urls, proxyUrl, outputFile, outputFormat) {
145
- const allResults = [];
146
- const errors = [];
147
-
148
- for (let i = 0; i < urls.length; i++) {
149
- const bar = ''.repeat(i + 1).padEnd(urls.length);
150
- process.stdout.write(`\r [${bar}] ${i + 1}/${urls.length}`);
151
- try {
152
- const results = await processUrl(urls[i], proxyUrl);
153
- allResults.push(...results);
154
- } catch (err) {
155
- errors.push({ url: urls[i], message: err.message });
156
- }
157
- }
158
- console.log();
159
-
160
- const uniqueResults = deduplicate(allResults);
161
-
162
- if (errors.length > 0) {
163
- if (uniqueResults.length === 0) {
164
- const msg = errors[0].message;
165
- if (msg.includes('不可用') || msg.includes('连接被拒绝') || msg.includes('连接中断') ||
166
- msg.includes('超时') || msg.includes('无法解析')) {
167
- console.error(` 所有请求失败,请检查代理是否可用: ${proxyUrl}\n`);
168
- } else {
169
- const show = errors.slice(0, 5);
170
- for (const e of show) {
171
- console.error(` ✗ ${e.url}: ${e.message}\n`);
172
- }
173
- if (errors.length > 5) {
174
- console.error(` ... 还有 ${errors.length - 5} 个失败\n`);
175
- }
176
- }
177
- console.log('未获取到数据');
178
- if (outputFile) {
179
- writeFileSync(outputFile, '[]', 'utf-8');
180
- }
181
- return;
182
- } else {
183
- const msg = errors[0].message;
184
- if (msg.includes('不可用') || msg.includes('连接被拒绝') || msg.includes('连接中断') ||
185
- msg.includes('超时') || msg.includes('无法解析')) {
186
- console.error(` ${errors.length} 个请求失败,请检查代理是否可用: ${proxyUrl}\n`);
187
- } else {
188
- console.error(` ${errors.length} 个失败:`);
189
- const show = errors.slice(0, 5);
190
- for (const e of show) {
191
- console.error(` ✗ ${e.url}: ${e.message}`);
192
- }
193
- if (errors.length > 5) {
194
- console.error(` ... 还有 ${errors.length - 5} 个`);
195
- }
196
- }
197
- }
198
- }
199
-
200
- const output = formatOutput(uniqueResults, outputFormat);
201
-
202
- if (outputFile) {
203
- writeFileSync(outputFile, output, 'utf-8');
204
- console.log(`\n结果已写入: ${outputFile}`);
205
- } else {
206
- console.log(output);
207
- }
208
- console.log(`\n共 ${uniqueResults.length} 个用户的数据`);
209
- }
210
-
211
- async function main() {
212
- const { urls, outputFile, outputFormat, exploreCount, showConfig: showCfg, showHelp, customProxy, configAction, configValue, pipeMode } = parseArgs();
213
- const proxyUrl = customProxy || proxy;
214
-
215
- if (showHelp) {
216
- showUsage();
217
- return;
218
- }
219
-
220
- if (configAction) {
221
- handleConfig(configAction, configValue);
222
- return;
223
- }
224
-
225
- if (showCfg) {
226
- showConfig(urls, outputFile);
227
- return;
228
- }
229
-
230
- if (urls.length === 0 && exploreCount === 0) {
231
- showUsage();
232
- return;
233
- }
234
-
235
- if (exploreCount > 0) {
236
- await runExplore(exploreCount, urls, proxyUrl, outputFile, outputFormat, pipeMode);
237
- } else {
238
- await runScrape(urls, proxyUrl, outputFile, outputFormat);
239
- }
240
- }
241
-
242
- main().catch(err => {
243
- console.error(`错误: ${err.message}`);
244
- process.exit(1);
245
- });
1
+ import { parseArgs } from './lib/args.js';
2
+ import { HELP_TEXT, CONFIG_TEXT, proxy, configFile, configPath, DEFAULT_PROXY, saveBrowser } from './lib/constants.js';
3
+ import { fetchExplore } from './lib/explore.js';
4
+ import { processUrl } from './lib/scrape.js';
5
+ import { deduplicate, formatOutput } from './lib/output.js';
6
+ import { parseFilter, applyFilter, formatFilterDescription } from './lib/filter.js';
7
+ import { writeFileSync, readFileSync, existsSync } from 'fs';
8
+
9
+ function showConfig(urls, outputFile) {
10
+ const lines = [...CONFIG_TEXT];
11
+ if (outputFile) lines.push(` 输出文件: ${outputFile}`);
12
+ if (urls.length > 0) lines.push(` 待处理URL: ${urls.length}`);
13
+ lines.push('', '参数:', ' -c, --config 显示当前配置', ' -h, --help 显示帮助');
14
+ console.log(lines.join('\n'));
15
+ }
16
+
17
+ function showUsage() {
18
+ console.log(HELP_TEXT.join('\n'));
19
+ process.exit(0);
20
+ }
21
+
22
+ function handleConfig(action, value) {
23
+ if (action === 'show' || action === null) {
24
+ showConfig([], null);
25
+ return;
26
+ }
27
+ if (action === 'set' || action === 'set-proxy') {
28
+ if (!value) {
29
+ console.error('用法: tt-help config set <代理地址>');
30
+ process.exit(1);
31
+ }
32
+ const cfg = existsSync(configPath) ? JSON.parse(readFileSync(configPath, 'utf-8')) : {};
33
+ cfg.proxy = value;
34
+ writeFileSync(configPath, JSON.stringify(cfg, null, 2), 'utf-8');
35
+ console.log(`代理已设置为: ${value}`);
36
+ console.log(`配置文件: ${configPath}`);
37
+ return;
38
+ }
39
+ if (action === 'set-browser') {
40
+ if (!value) {
41
+ console.error('用法: tt-help config set-browser <浏览器路径 或 auto>');
42
+ process.exit(1);
43
+ }
44
+ if (value === 'auto') {
45
+ if (existsSync(configPath)) {
46
+ const cfg = JSON.parse(readFileSync(configPath, 'utf-8'));
47
+ delete cfg.browser;
48
+ writeFileSync(configPath, JSON.stringify(cfg, null, 2), 'utf-8');
49
+ }
50
+ console.log('已切换为自动探测浏览器模式');
51
+ } else {
52
+ saveBrowser(value);
53
+ console.log(`浏览器已设置为: ${value}`);
54
+ }
55
+ console.log(`配置文件: ${configPath}`);
56
+ return;
57
+ }
58
+ if (action === 'reset') {
59
+ if (existsSync(configPath)) {
60
+ const cfg = JSON.parse(readFileSync(configPath, 'utf-8'));
61
+ cfg.proxy = DEFAULT_PROXY;
62
+ writeFileSync(configPath, JSON.stringify(cfg, null, 2), 'utf-8');
63
+ console.log(`已恢复默认代理: ${DEFAULT_PROXY}`);
64
+ console.log(`配置文件: ${configPath}`);
65
+ } else {
66
+ console.log('当前使用默认代理,无需重置');
67
+ }
68
+ return;
69
+ }
70
+ console.error(`未知配置命令: ${action}`);
71
+ console.error('用法: tt-help config [show|set|set-browser|reset]');
72
+ process.exit(1);
73
+ }
74
+
75
+ function cleanError(msg) {
76
+ return msg
77
+ .replace(/\x1b\[[0-9;]*m/g, '')
78
+ .replace(/\s*- navigating to.*/s, '')
79
+ .replace(/\s*Call log:/s, '')
80
+ .trim();
81
+ }
82
+
83
+ async function runExplore(exploreCount, urls, proxyUrl, outputFile, outputFormat, isPipe, filter) {
84
+ console.log(`\n代理: ${proxyUrl}`);
85
+ console.log(`Explore 数量: ${exploreCount}`);
86
+ if (urls.length > 0) {
87
+ console.log(`额外 URL: ${urls.length}\n`);
88
+ } else {
89
+ console.log('');
90
+ }
91
+
92
+ const allResults = [];
93
+
94
+ if (exploreCount > 0) {
95
+ try {
96
+ const exploreResults = await fetchExplore(exploreCount);
97
+ console.log(` 获取到 ${exploreResults.length} 个视频\n`);
98
+ if (isPipe) {
99
+ const videoUrls = exploreResults.map(r => r.url).filter(Boolean);
100
+ if (videoUrls.length > 0) {
101
+ await runScrape(videoUrls, proxyUrl, outputFile, outputFormat, filter);
102
+ return;
103
+ }
104
+ }
105
+ allResults.push(...exploreResults);
106
+ } catch (err) {
107
+ console.error(` Explore 获取失败: ${cleanError(err.message)}\n`);
108
+ console.error(` 请确保代理 ${proxyUrl} 正常运行\n`);
109
+ }
110
+ }
111
+
112
+ if (urls.length > 0) {
113
+ const errors = [];
114
+
115
+ for (let i = 0; i < urls.length; i++) {
116
+ const bar = '█'.repeat(i + 1).padEnd(urls.length);
117
+ process.stdout.write(`\r [${bar}] ${i + 1}/${urls.length}`);
118
+ try {
119
+ const results = await processUrl(urls[i], proxyUrl);
120
+ allResults.push(...results);
121
+ } catch (err) {
122
+ errors.push({ url: urls[i], message: err.message });
123
+ }
124
+ }
125
+ console.log();
126
+
127
+ if (errors.length > 0) {
128
+ const msg = errors[0].message;
129
+ if (msg.includes('不可用') || msg.includes('连接被拒绝') || msg.includes('连接中断') ||
130
+ msg.includes('超时') || msg.includes('无法解析')) {
131
+ console.error(` ${errors.length} 个请求失败,请检查代理是否可用: ${proxyUrl}\n`);
132
+ } else {
133
+ console.error(` ${errors.length} 个失败:`);
134
+ const show = errors.slice(0, 5);
135
+ for (const e of show) {
136
+ console.error(` ${e.url}: ${e.message}`);
137
+ }
138
+ if (errors.length > 5) {
139
+ console.error(` ... 还有 ${errors.length - 5} 个`);
140
+ }
141
+ }
142
+ }
143
+ }
144
+
145
+ const uniqueResults = deduplicate(allResults);
146
+ const filteredResults = applyFilter(uniqueResults, filter);
147
+
148
+ if (filteredResults.length === 0) {
149
+ console.log('\n未获取到数据');
150
+ if (outputFile) {
151
+ writeFileSync(outputFile, '[]', 'utf-8');
152
+ }
153
+ return;
154
+ }
155
+
156
+ const output = formatOutput(filteredResults, outputFormat);
157
+
158
+ if (outputFile) {
159
+ writeFileSync(outputFile, output, 'utf-8');
160
+ console.log(`\n结果已写入: ${outputFile}`);
161
+ } else {
162
+ console.log(output);
163
+ }
164
+
165
+ if (filter) {
166
+ console.log(`\n共 ${uniqueResults.length} 个数据,过滤后 ${filteredResults.length} 个(过滤条件: ${formatFilterDescription(filter)})`);
167
+ } else {
168
+ console.log(`\n共 ${filteredResults.length} 个数据`);
169
+ }
170
+ }
171
+
172
+ async function runScrape(urls, proxyUrl, outputFile, outputFormat, filter) {
173
+ const allResults = [];
174
+ const errors = [];
175
+
176
+ for (let i = 0; i < urls.length; i++) {
177
+ const bar = '█'.repeat(i + 1).padEnd(urls.length);
178
+ process.stdout.write(`\r [${bar}] ${i + 1}/${urls.length}`);
179
+ try {
180
+ const results = await processUrl(urls[i], proxyUrl);
181
+ allResults.push(...results);
182
+ } catch (err) {
183
+ errors.push({ url: urls[i], message: err.message });
184
+ }
185
+ }
186
+ console.log();
187
+
188
+ const uniqueResults = deduplicate(allResults);
189
+ const filteredResults = applyFilter(uniqueResults, filter);
190
+
191
+ if (errors.length > 0) {
192
+ if (filteredResults.length === 0) {
193
+ const msg = errors[0].message;
194
+ if (msg.includes('不可用') || msg.includes('连接被拒绝') || msg.includes('连接中断') ||
195
+ msg.includes('超时') || msg.includes('无法解析')) {
196
+ console.error(` 所有请求失败,请检查代理是否可用: ${proxyUrl}\n`);
197
+ } else {
198
+ const show = errors.slice(0, 5);
199
+ for (const e of show) {
200
+ console.error(` ${e.url}: ${e.message}\n`);
201
+ }
202
+ if (errors.length > 5) {
203
+ console.error(` ... 还有 ${errors.length - 5} 个失败\n`);
204
+ }
205
+ }
206
+ console.log('未获取到数据');
207
+ if (outputFile) {
208
+ writeFileSync(outputFile, '[]', 'utf-8');
209
+ }
210
+ return;
211
+ } else {
212
+ const msg = errors[0].message;
213
+ if (msg.includes('不可用') || msg.includes('连接被拒绝') || msg.includes('连接中断') ||
214
+ msg.includes('超时') || msg.includes('无法解析')) {
215
+ console.error(` ${errors.length} 个请求失败,请检查代理是否可用: ${proxyUrl}\n`);
216
+ } else {
217
+ console.error(` ${errors.length} 个失败:`);
218
+ const show = errors.slice(0, 5);
219
+ for (const e of show) {
220
+ console.error(` ✗ ${e.url}: ${e.message}`);
221
+ }
222
+ if (errors.length > 5) {
223
+ console.error(` ... 还有 ${errors.length - 5} 个`);
224
+ }
225
+ }
226
+ }
227
+ }
228
+
229
+ const output = formatOutput(filteredResults, outputFormat);
230
+
231
+ if (outputFile) {
232
+ writeFileSync(outputFile, output, 'utf-8');
233
+ console.log(`\n结果已写入: ${outputFile}`);
234
+ } else {
235
+ console.log(output);
236
+ }
237
+
238
+ if (filter) {
239
+ console.log(`\n共 ${uniqueResults.length} 个数据,过滤后 ${filteredResults.length} 个(过滤条件: ${formatFilterDescription(filter)})`);
240
+ } else {
241
+ console.log(`\n共 ${filteredResults.length} 个用户的数据`);
242
+ }
243
+ }
244
+
245
+ async function main() {
246
+ const { urls, outputFile, outputFormat, exploreCount, showConfig: showCfg, showHelp, customProxy, configAction, configValue, pipeMode, filterStr } = parseArgs();
247
+ const proxyUrl = customProxy || proxy;
248
+ const filter = parseFilter(filterStr);
249
+
250
+ if (showHelp) {
251
+ showUsage();
252
+ return;
253
+ }
254
+
255
+ if (configAction) {
256
+ handleConfig(configAction, configValue);
257
+ return;
258
+ }
259
+
260
+ if (showCfg) {
261
+ showConfig(urls, outputFile);
262
+ return;
263
+ }
264
+
265
+ if (urls.length === 0 && exploreCount === 0) {
266
+ showUsage();
267
+ return;
268
+ }
269
+
270
+ if (exploreCount > 0) {
271
+ await runExplore(exploreCount, urls, proxyUrl, outputFile, outputFormat, pipeMode, filter);
272
+ } else {
273
+ await runScrape(urls, proxyUrl, outputFile, outputFormat, filter);
274
+ }
275
+ }
276
+
277
+ main().catch(err => {
278
+ console.error(`错误: ${err.message}`);
279
+ process.exit(1);
280
+ });