tt-help-cli-ycl 1.3.49 → 1.3.51
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/cli/open.js +3 -5
- package/src/lib/tiktok-scraper.mjs +42 -48
package/package.json
CHANGED
package/src/cli/open.js
CHANGED
|
@@ -33,15 +33,13 @@ export async function handleOpen(parsed) {
|
|
|
33
33
|
if (!openPort) {
|
|
34
34
|
console.error("用法: tt-help open <端口>");
|
|
35
35
|
console.error("示例: tt-help open 9222");
|
|
36
|
-
console.error("");
|
|
37
|
-
console.error("可用端口: 9222 - 9231 (共 10 个)");
|
|
38
|
-
console.error('运行 "tt-help open --list" 查看所有配置');
|
|
36
|
+
console.error('运行 "tt-help open --list" 查看所有内置配置');
|
|
39
37
|
process.exit(1);
|
|
40
38
|
}
|
|
41
39
|
|
|
42
40
|
const port = parseInt(openPort);
|
|
43
|
-
if (isNaN(port) || port <
|
|
44
|
-
console.error(`端口 ${openPort}
|
|
41
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
42
|
+
console.error(`端口 ${openPort} 无效,请输入 1-65535 之间的端口号`);
|
|
45
43
|
process.exit(1);
|
|
46
44
|
}
|
|
47
45
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os from "os";
|
|
2
2
|
import path from "path";
|
|
3
|
+
import fs from "fs";
|
|
3
4
|
import { chromium } from "playwright";
|
|
4
5
|
import { detectBrowser } from "./browser/launch.js";
|
|
5
6
|
import { parseUserInfo, parseVideoInfo } from "./parse-ssr.mjs";
|
|
@@ -9,7 +10,7 @@ const DEFAULT_WAF_TTL = 120000;
|
|
|
9
10
|
const DEFAULT_WARM_URL = "https://www.tiktok.com/@nike";
|
|
10
11
|
const BROWSER_CLOSE_TIMEOUT = 5000;
|
|
11
12
|
const DEFAULT_MAX_REQUESTS_PER_PAGE = 50;
|
|
12
|
-
const FALLBACK_PROFILE_PORT =
|
|
13
|
+
const FALLBACK_PROFILE_PORT = 9999;
|
|
13
14
|
|
|
14
15
|
function delay(ms) {
|
|
15
16
|
return new Promise((r) => setTimeout(r, ms));
|
|
@@ -120,10 +121,11 @@ export class TikTokScraper {
|
|
|
120
121
|
this.slotIdx = 0;
|
|
121
122
|
this.lastWarmTime = 0;
|
|
122
123
|
this.warmPromise = null;
|
|
123
|
-
|
|
124
|
-
this.
|
|
125
|
-
this.
|
|
126
|
-
this.
|
|
124
|
+
// 登录态 pool(init 时直接启动)
|
|
125
|
+
this.authBrowser = null;
|
|
126
|
+
this.authContext = null;
|
|
127
|
+
this.authSlots = [];
|
|
128
|
+
this.authSlotIdx = 0;
|
|
127
129
|
}
|
|
128
130
|
|
|
129
131
|
async init() {
|
|
@@ -141,23 +143,26 @@ export class TikTokScraper {
|
|
|
141
143
|
this.browser = browser;
|
|
142
144
|
this.context = context;
|
|
143
145
|
this.slots = slots;
|
|
144
|
-
await this.warmWaf();
|
|
145
|
-
}
|
|
146
146
|
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
// 启动登录态 pool(1 个 slot)
|
|
148
|
+
// profile 不存在则跳过,不影响主流程
|
|
149
149
|
const fallbackDir = getFallbackProfileDir();
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
1,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
150
|
+
if (fs.existsSync(fallbackDir)) {
|
|
151
|
+
const {
|
|
152
|
+
browser: authBrowser,
|
|
153
|
+
context: authContext,
|
|
154
|
+
slots: authSlots,
|
|
155
|
+
} = await initContext(executablePath, 1, fallbackDir);
|
|
156
|
+
this.authBrowser = authBrowser;
|
|
157
|
+
this.authContext = authContext;
|
|
158
|
+
this.authSlots = authSlots;
|
|
159
|
+
} else {
|
|
160
|
+
console.error(
|
|
161
|
+
`[TikTokScraper] 登录态 profile 不存在 (${fallbackDir}),跳过登录态 pool。请先运行 tt-help open 9999 登录 TikTok`,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
await this.warmWaf();
|
|
161
166
|
}
|
|
162
167
|
|
|
163
168
|
async close() {
|
|
@@ -183,20 +188,20 @@ export class TikTokScraper {
|
|
|
183
188
|
}
|
|
184
189
|
};
|
|
185
190
|
|
|
186
|
-
// 无登录态的 browser
|
|
191
|
+
// 无登录态的 browser
|
|
187
192
|
await closeAll(this.browser);
|
|
188
|
-
//
|
|
189
|
-
if (this.
|
|
193
|
+
// 登录态的 context(launchPersistentContext 返回的是 context 当 browser)
|
|
194
|
+
if (this.authContext) {
|
|
190
195
|
try {
|
|
191
|
-
await this.
|
|
196
|
+
await this.authContext.close();
|
|
192
197
|
} catch {}
|
|
193
198
|
}
|
|
194
199
|
this.browser = null;
|
|
195
200
|
this.context = null;
|
|
196
201
|
this.slots = [];
|
|
197
|
-
this.
|
|
198
|
-
this.
|
|
199
|
-
this.
|
|
202
|
+
this.authBrowser = null;
|
|
203
|
+
this.authContext = null;
|
|
204
|
+
this.authSlots = [];
|
|
200
205
|
}
|
|
201
206
|
|
|
202
207
|
async restart() {
|
|
@@ -237,11 +242,6 @@ export class TikTokScraper {
|
|
|
237
242
|
return Date.now() - this.lastWarmTime > this.wafTtl;
|
|
238
243
|
}
|
|
239
244
|
|
|
240
|
-
_pickSlot(slots, idx, setIdx) {
|
|
241
|
-
const slot = slots[idx % slots.length];
|
|
242
|
-
return slot;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
245
|
async _ensurePage(slot, context) {
|
|
246
246
|
try {
|
|
247
247
|
if (
|
|
@@ -258,13 +258,11 @@ export class TikTokScraper {
|
|
|
258
258
|
}
|
|
259
259
|
|
|
260
260
|
async _fetchViewSource(url, ctx) {
|
|
261
|
-
const slots =
|
|
262
|
-
|
|
263
|
-
const slotIdx =
|
|
264
|
-
ctx === this._fallbackContext ? this._fallbackSlotIdx : this.slotIdx;
|
|
261
|
+
const slots = ctx === this.authContext ? this.authSlots : this.slots;
|
|
262
|
+
const slotIdx = ctx === this.authContext ? this.authSlotIdx : this.slotIdx;
|
|
265
263
|
const slot = slots[slotIdx % slots.length];
|
|
266
|
-
if (ctx === this.
|
|
267
|
-
this.
|
|
264
|
+
if (ctx === this.authContext) {
|
|
265
|
+
this.authSlotIdx++;
|
|
268
266
|
} else {
|
|
269
267
|
this.slotIdx++;
|
|
270
268
|
}
|
|
@@ -329,11 +327,9 @@ export class TikTokScraper {
|
|
|
329
327
|
result = rawHtml ? parseUserInfo(rawHtml) : null;
|
|
330
328
|
}
|
|
331
329
|
|
|
332
|
-
//
|
|
333
|
-
if (!result) {
|
|
334
|
-
|
|
335
|
-
await this._ensureFallback(executablePath);
|
|
336
|
-
rawHtml = await this._fetchViewSource(url, this._fallbackContext);
|
|
330
|
+
// 仍然失败:使用登录态 pool
|
|
331
|
+
if (!result && this.authContext) {
|
|
332
|
+
rawHtml = await this._fetchViewSource(url, this.authContext);
|
|
337
333
|
result = rawHtml ? parseUserInfo(rawHtml) : null;
|
|
338
334
|
}
|
|
339
335
|
|
|
@@ -354,11 +350,9 @@ export class TikTokScraper {
|
|
|
354
350
|
result = rawHtml ? parseVideoInfo(rawHtml) : null;
|
|
355
351
|
}
|
|
356
352
|
|
|
357
|
-
//
|
|
358
|
-
if (!result) {
|
|
359
|
-
|
|
360
|
-
await this._ensureFallback(executablePath);
|
|
361
|
-
rawHtml = await this._fetchViewSource(videoUrl, this._fallbackContext);
|
|
353
|
+
// 仍然失败:使用登录态 pool
|
|
354
|
+
if (!result && this.authContext) {
|
|
355
|
+
rawHtml = await this._fetchViewSource(videoUrl, this.authContext);
|
|
362
356
|
result = rawHtml ? parseVideoInfo(rawHtml) : null;
|
|
363
357
|
}
|
|
364
358
|
|