tt-help-cli-ycl 1.0.5 → 1.0.6
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/package.json +1 -1
- package/src/lib/io.js +63 -0
- package/src/main.mjs +88 -17
package/package.json
CHANGED
package/src/lib/io.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { writeFileSync, readFileSync } from 'fs';
|
|
2
2
|
|
|
3
|
+
let lastBarCount = 0;
|
|
4
|
+
|
|
3
5
|
export function writeOutput(data, outputFile) {
|
|
4
6
|
const output = JSON.stringify(data, null, 2);
|
|
5
7
|
const target = outputFile || 'tiktok_data.json';
|
|
@@ -11,3 +13,64 @@ export function readUrlFile(filePath) {
|
|
|
11
13
|
const content = readFileSync(filePath, 'utf-8');
|
|
12
14
|
return content.split(/\r?\n/).map(l => l.trim()).filter(l => l.startsWith('http'));
|
|
13
15
|
}
|
|
16
|
+
|
|
17
|
+
export function createProgressBar(current, total, maxWidth = 30) {
|
|
18
|
+
const filled = Math.round((current / total) * maxWidth);
|
|
19
|
+
return '█'.repeat(filled).padEnd(maxWidth);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function calculateConcurrency(total) {
|
|
23
|
+
return Math.min(5, Math.max(1, Math.floor(total / 10)), total);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function extractUrlDisplay(url) {
|
|
27
|
+
try {
|
|
28
|
+
const pathname = new URL(url).pathname;
|
|
29
|
+
const parts = pathname.split('/').filter(Boolean);
|
|
30
|
+
return parts.slice(-2).join('/');
|
|
31
|
+
} catch {
|
|
32
|
+
return url;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function createMultiProgressBars(count) {
|
|
37
|
+
return Array.from({ length: count }, () => ({
|
|
38
|
+
current: 0,
|
|
39
|
+
total: 0,
|
|
40
|
+
status: 'pending',
|
|
41
|
+
url: '',
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function renderMultiProgressBars(bars, maxWidth = 30) {
|
|
46
|
+
const activeBars = bars.filter(bar => bar.total > 0);
|
|
47
|
+
|
|
48
|
+
if (activeBars.length === 0) return;
|
|
49
|
+
|
|
50
|
+
const lines = activeBars.map((bar) => {
|
|
51
|
+
const prog = createProgressBar(bar.current, bar.total, maxWidth);
|
|
52
|
+
const icon = bar.status === 'done' ? '✓' :
|
|
53
|
+
bar.status === 'error' ? '' : '⟳';
|
|
54
|
+
const urlDisplay = bar.url ? extractUrlDisplay(bar.url) : '';
|
|
55
|
+
return ` [${prog}] ${bar.current}/${bar.total} ${icon} ${urlDisplay}`;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const output = lines.join('\n');
|
|
59
|
+
|
|
60
|
+
if (lastBarCount > 0) {
|
|
61
|
+
process.stdout.write(`\x1b[${lastBarCount}A`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
process.stdout.write('\x1b[0J');
|
|
65
|
+
process.stdout.write(output + '\n');
|
|
66
|
+
|
|
67
|
+
lastBarCount = activeBars.length;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function clearProgressBars() {
|
|
71
|
+
if (lastBarCount > 0) {
|
|
72
|
+
process.stdout.write(`\x1b[${lastBarCount}A`);
|
|
73
|
+
process.stdout.write('\x1b[0J');
|
|
74
|
+
lastBarCount = 0;
|
|
75
|
+
}
|
|
76
|
+
}
|
package/src/main.mjs
CHANGED
|
@@ -4,6 +4,7 @@ import { fetchExplore } from './lib/explore.js';
|
|
|
4
4
|
import { processUrl } from './lib/scrape.js';
|
|
5
5
|
import { deduplicate, formatOutput } from './lib/output.js';
|
|
6
6
|
import { parseFilter, applyFilter, formatFilterDescription } from './lib/filter.js';
|
|
7
|
+
import { createProgressBar, calculateConcurrency, createMultiProgressBars, renderMultiProgressBars, clearProgressBars } from './lib/io.js';
|
|
7
8
|
import { writeFileSync, readFileSync, existsSync } from 'fs';
|
|
8
9
|
|
|
9
10
|
function showConfig(urls, outputFile) {
|
|
@@ -72,6 +73,10 @@ function handleConfig(action, value) {
|
|
|
72
73
|
process.exit(1);
|
|
73
74
|
}
|
|
74
75
|
|
|
76
|
+
function randomDelay() {
|
|
77
|
+
return new Promise(r => setTimeout(r, Math.random() * 600 + 200));
|
|
78
|
+
}
|
|
79
|
+
|
|
75
80
|
function cleanError(msg) {
|
|
76
81
|
return msg
|
|
77
82
|
.replace(/\x1b\[[0-9;]*m/g, '')
|
|
@@ -112,16 +117,46 @@ async function runExplore(exploreCount, urls, proxyUrl, outputFile, outputFormat
|
|
|
112
117
|
if (urls.length > 0) {
|
|
113
118
|
const errors = [];
|
|
114
119
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
const concurrency = calculateConcurrency(urls.length);
|
|
121
|
+
const bars = createMultiProgressBars(concurrency);
|
|
122
|
+
|
|
123
|
+
const slots = Array.from({ length: concurrency }, () => []);
|
|
124
|
+
urls.forEach((url, i) => slots[i % concurrency].push(url));
|
|
125
|
+
|
|
126
|
+
bars.forEach((bar, i) => {
|
|
127
|
+
bar.total = slots[i].length;
|
|
128
|
+
bar.status = slots[i].length > 0 ? 'running' : 'done';
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
renderMultiProgressBars(bars);
|
|
132
|
+
|
|
133
|
+
const workers = slots.map(async (slotUrls, slotIndex) => {
|
|
134
|
+
for (const url of slotUrls) {
|
|
135
|
+
bars[slotIndex].url = url;
|
|
136
|
+
renderMultiProgressBars(bars);
|
|
137
|
+
|
|
138
|
+
await randomDelay();
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
const results = await processUrl(url, proxyUrl);
|
|
142
|
+
allResults.push(...results);
|
|
143
|
+
bars[slotIndex].current++;
|
|
144
|
+
bars[slotIndex].status = 'running';
|
|
145
|
+
} catch (err) {
|
|
146
|
+
errors.push({ url, message: err.message });
|
|
147
|
+
bars[slotIndex].current++;
|
|
148
|
+
bars[slotIndex].status = 'error';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
renderMultiProgressBars(bars);
|
|
123
152
|
}
|
|
124
|
-
|
|
153
|
+
bars[slotIndex].status = bars[slotIndex].current === bars[slotIndex].total ? 'done' : 'error';
|
|
154
|
+
renderMultiProgressBars(bars);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
await Promise.all(workers);
|
|
158
|
+
|
|
159
|
+
clearProgressBars();
|
|
125
160
|
console.log();
|
|
126
161
|
|
|
127
162
|
if (errors.length > 0) {
|
|
@@ -173,16 +208,52 @@ async function runScrape(urls, proxyUrl, outputFile, outputFormat, filter) {
|
|
|
173
208
|
const allResults = [];
|
|
174
209
|
const errors = [];
|
|
175
210
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const results = await processUrl(urls[i], proxyUrl);
|
|
181
|
-
allResults.push(...results);
|
|
182
|
-
} catch (err) {
|
|
183
|
-
errors.push({ url: urls[i], message: err.message });
|
|
211
|
+
if (urls.length === 0) {
|
|
212
|
+
console.log('\n未获取到数据');
|
|
213
|
+
if (outputFile) {
|
|
214
|
+
writeFileSync(outputFile, '[]', 'utf-8');
|
|
184
215
|
}
|
|
216
|
+
return;
|
|
185
217
|
}
|
|
218
|
+
|
|
219
|
+
const concurrency = calculateConcurrency(urls.length);
|
|
220
|
+
const bars = createMultiProgressBars(concurrency);
|
|
221
|
+
|
|
222
|
+
const slots = Array.from({ length: concurrency }, () => []);
|
|
223
|
+
urls.forEach((url, i) => slots[i % concurrency].push(url));
|
|
224
|
+
|
|
225
|
+
bars.forEach((bar, i) => {
|
|
226
|
+
bar.total = slots[i].length;
|
|
227
|
+
bar.status = slots[i].length > 0 ? 'running' : 'done';
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
renderMultiProgressBars(bars);
|
|
231
|
+
|
|
232
|
+
const workers = slots.map(async (slotUrls, slotIndex) => {
|
|
233
|
+
for (const url of slotUrls) {
|
|
234
|
+
bars[slotIndex].url = url;
|
|
235
|
+
renderMultiProgressBars(bars);
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
const results = await processUrl(url, proxyUrl);
|
|
239
|
+
allResults.push(...results);
|
|
240
|
+
bars[slotIndex].current++;
|
|
241
|
+
bars[slotIndex].status = 'running';
|
|
242
|
+
} catch (err) {
|
|
243
|
+
errors.push({ url, message: err.message });
|
|
244
|
+
bars[slotIndex].current++;
|
|
245
|
+
bars[slotIndex].status = 'error';
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
renderMultiProgressBars(bars);
|
|
249
|
+
}
|
|
250
|
+
bars[slotIndex].status = bars[slotIndex].current === bars[slotIndex].total ? 'done' : 'error';
|
|
251
|
+
renderMultiProgressBars(bars);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
await Promise.all(workers);
|
|
255
|
+
|
|
256
|
+
clearProgressBars();
|
|
186
257
|
console.log();
|
|
187
258
|
|
|
188
259
|
const uniqueResults = deduplicate(allResults);
|