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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tt-help-cli-ycl",
3
- "version": "1.3.49",
3
+ "version": "1.3.51",
4
4
  "description": "TikTok user & video data scraper - extract ttSeller, verified, locationCreated from HTML source",
5
5
  "type": "module",
6
6
  "bin": {
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 < BASE_PORT || port >= BASE_PORT + TOTAL_ACCOUNTS) {
44
- console.error(`端口 ${openPort} 不在有效范围内 (9222 - 9231)`);
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 = 9222;
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
- this._fallbackBrowser = null;
124
- this._fallbackContext = null;
125
- this._fallbackSlots = [];
126
- this._fallbackSlotIdx = 0;
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
- async _ensureFallback(executablePath) {
148
- if (this._fallbackContext) return;
147
+ // 启动登录态 pool(1 个 slot)
148
+ // profile 不存在则跳过,不影响主流程
149
149
  const fallbackDir = getFallbackProfileDir();
150
- console.error(
151
- `[TikTokScraper] 无登录态获取失败,fallback 到登录态 profile: ${fallbackDir}`,
152
- );
153
- const { browser, context, slots } = await initContext(
154
- executablePath,
155
- 1,
156
- fallbackDir,
157
- );
158
- this._fallbackBrowser = browser;
159
- this._fallbackContext = context;
160
- this._fallbackSlots = slots;
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 是 chromium.launch() 返回的,直接 close
191
+ // 无登录态的 browser
187
192
  await closeAll(this.browser);
188
- // fallback context launchPersistentContext 返回的,直接 close
189
- if (this._fallbackContext) {
193
+ // 登录态的 context(launchPersistentContext 返回的是 context browser)
194
+ if (this.authContext) {
190
195
  try {
191
- await this._fallbackContext.close();
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._fallbackBrowser = null;
198
- this._fallbackContext = null;
199
- this._fallbackSlots = [];
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
- ctx === this._fallbackContext ? this._fallbackSlots : this.slots;
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._fallbackContext) {
267
- this._fallbackSlotIdx++;
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
- // 仍然失败:fallback 到登录态
333
- if (!result) {
334
- const executablePath = detectBrowser();
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
- // 仍然失败:fallback 到登录态
358
- if (!result) {
359
- const executablePath = detectBrowser();
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