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.
- package/README.md +33 -33
- package/cli.js +9 -9
- package/package.json +52 -52
- package/scripts/run-explore copy.bat +101 -101
- package/scripts/run-explore.bat +134 -134
- package/scripts/run-explore.ps1 +159 -159
- package/scripts/run-explore.sh +121 -121
- package/scripts/test-captcha-lib.mjs +68 -0
- package/scripts/test-captcha.mjs +81 -0
- package/scripts/test-incognito-lib.mjs +36 -0
- package/scripts/test-login-state.mjs +128 -0
- package/scripts/test-safe-click.mjs +45 -0
- package/scripts/test-watch-db-smoke.mjs +246 -0
- package/src/cli/attach.js +331 -331
- package/src/cli/auto.js +265 -265
- package/src/cli/comments.js +620 -620
- package/src/cli/config.js +170 -170
- package/src/cli/db-import.js +51 -51
- package/src/cli/explore.js +555 -555
- package/src/cli/open.js +109 -111
- package/src/cli/progress.js +111 -111
- package/src/cli/refresh.js +288 -288
- package/src/cli/scrape.js +47 -47
- package/src/cli/utils.js +18 -18
- package/src/cli/videos.js +41 -41
- package/src/cli/videostats.js +196 -196
- package/src/cli/watch.js +30 -30
- package/src/lib/api-interceptor.js +161 -161
- package/src/lib/args.js +809 -809
- package/src/lib/browser/anti-detect.js +23 -23
- package/src/lib/browser/cdp.js +261 -261
- package/src/lib/browser/health-checker.js +114 -114
- package/src/lib/browser/launch.js +43 -43
- package/src/lib/browser/page.js +184 -184
- package/src/lib/constants.js +297 -297
- package/src/lib/delay.js +54 -54
- package/src/lib/explore-fetch.js +118 -118
- package/src/lib/fetcher.js +45 -45
- package/src/lib/filter.js +66 -66
- package/src/lib/io.js +54 -54
- package/src/lib/output.js +80 -80
- package/src/lib/page-error-detector.js +109 -109
- package/src/lib/parse-ssr.mjs +69 -69
- package/src/lib/parser.js +47 -47
- package/src/lib/retry.js +45 -45
- package/src/lib/scrape.js +90 -90
- package/src/lib/target-locations.js +61 -61
- package/src/lib/tiktok-scraper.mjs +98 -61
- package/src/lib/url.js +52 -52
- package/src/main.js +73 -73
- package/src/npm-main.js +70 -70
- package/src/results/user-videos-bar.lar.lar.moeta.json +37 -0
- package/src/scraper/auto-core.js +203 -203
- package/src/scraper/core.js +255 -255
- package/src/scraper/explore-core.js +208 -208
- package/src/scraper/modules/captcha-handler.js +114 -114
- package/src/scraper/modules/follow-extractor.js +250 -250
- package/src/scraper/modules/guess-extractor.js +51 -51
- package/src/scraper/modules/page-helpers.js +48 -48
- package/src/scraper/refresh-core.js +213 -213
- package/src/videos/core.js +143 -143
- package/src/watch/data-store.js +2980 -2980
- package/src/watch/public/index.html +2355 -2355
- 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(
|
|
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
|
-
|
|
127
|
-
this.
|
|
128
|
-
this.
|
|
129
|
-
this.
|
|
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(
|
|
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
|
-
|
|
147
|
-
if (this._fallbackContext) return;
|
|
148
|
-
if (!fallbackProfileExists()) {
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
146
|
+
// 启动登录态 pool(1 个 slot)
|
|
151
147
|
const fallbackDir = getFallbackProfileDir();
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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(() => {
|
|
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
|
|
183
|
+
// 无登录态的 browser
|
|
182
184
|
await closeAll(this.browser);
|
|
183
|
-
//
|
|
184
|
-
if (this.
|
|
185
|
-
try {
|
|
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.
|
|
191
|
-
this.
|
|
192
|
-
this.
|
|
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 (
|
|
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.
|
|
252
|
-
const
|
|
253
|
-
|
|
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
|
-
|
|
265
|
+
|
|
259
266
|
if (ctx === this.context && this.isWarmExpired()) {
|
|
260
267
|
await this.warmWaf();
|
|
261
268
|
}
|
|
262
|
-
|
|
269
|
+
|
|
270
|
+
await page.goto("view-source:" + url, {
|
|
263
271
|
waitUntil: "domcontentloaded",
|
|
264
272
|
timeout: 15000,
|
|
265
273
|
});
|
|
266
|
-
|
|
267
|
-
|
|
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
|
-
//
|
|
313
|
+
// 解析失败:先尝试 warmWaf 后重试
|
|
287
314
|
if (!result) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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
|
-
//
|
|
336
|
+
// 解析失败:先尝试 warmWaf 后重试
|
|
306
337
|
if (!result) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|
+
});
|