tt-help-cli-ycl 1.4.44 → 1.4.46

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.4.44",
3
+ "version": "1.4.46",
4
4
  "description": "TikTok user & video data scraper - extract ttSeller, verified, locationCreated from HTML source",
5
5
  "type": "module",
6
6
  "bin": {
@@ -17,6 +17,7 @@
17
17
  */
18
18
 
19
19
  import { ProxyAgent, request } from "undici";
20
+ import { execSync } from "child_process";
20
21
  import { DEFAULT_PROXY } from "./constants.js";
21
22
 
22
23
  const PDP_HEADERS = {
@@ -80,6 +81,116 @@ function extractJsonByKey(html, keyPattern) {
80
81
  return null;
81
82
  }
82
83
 
84
+ /**
85
+ * Windows: 后台服务的 Playwright 浏览器在 Session 0(隔离桌面),
86
+ * RDP 看不到。找到交互桌面 Session,用计划任务推到那个 Session。
87
+ */
88
+ let winRecoveryRunning = false;
89
+
90
+ async function resolveCaptchaWindows(url, proxyUrl) {
91
+ if (winRecoveryRunning) {
92
+ console.error("[shop-scraper] Windows: 验证码恢复已在运行,跳过");
93
+ return false;
94
+ }
95
+ winRecoveryRunning = true;
96
+
97
+ try {
98
+ const edgeUrl = url.replace("shop.tiktok.com", "www.tiktok.com/shop/pdp");
99
+ const taskName = "TikTokCaptchaRecovery";
100
+
101
+ // 1. 找到交互桌面 Session
102
+ let sessionId = null;
103
+ try {
104
+ const sessions = execSync(
105
+ `powershell -NoProfile -Command "query session 2>$null | Select-String 'Active' | ForEach-Object { \\$_.Line }"`,
106
+ { shell: "cmd.exe", timeout: 5000, encoding: "utf-8" },
107
+ );
108
+ const lines = sessions.trim().split("\n");
109
+ for (const line of lines) {
110
+ const match = line.match(/>\s*(\d+)\s/);
111
+ if (match && match[1] !== "0") {
112
+ sessionId = parseInt(match[1], 10);
113
+ break;
114
+ }
115
+ }
116
+ } catch {
117
+ console.error("[shop-scraper] Windows: 无法获取交互 Session");
118
+ }
119
+
120
+ if (sessionId) {
121
+ console.error(
122
+ `[shop-scraper] Windows: Session ${sessionId},用计划任务启动 Edge`,
123
+ );
124
+ try {
125
+ execSync(`schtasks /delete /tn "${taskName}" /f 2>$null`, {
126
+ shell: "cmd.exe",
127
+ });
128
+ } catch {}
129
+ execSync(
130
+ `schtasks /create /tn "${taskName}" /tr "msedge ${edgeUrl}" /sc once /st 00:00 /it /f`,
131
+ { shell: "cmd.exe", timeout: 10000 },
132
+ );
133
+ execSync(`schtasks /run /tn "${taskName}"`, {
134
+ shell: "cmd.exe",
135
+ timeout: 10000,
136
+ });
137
+ setTimeout(() => {
138
+ try {
139
+ execSync(`schtasks /delete /tn "${taskName}" /f 2>$null`, {
140
+ shell: "cmd.exe",
141
+ });
142
+ } catch {}
143
+ }, 10000);
144
+ } else {
145
+ console.error(
146
+ "[shop-scraper] Windows: 未找到交互 Session,尝试直接 start",
147
+ );
148
+ try {
149
+ execSync(`start "" "microsoft-edge:${edgeUrl}"`, {
150
+ shell: "cmd.exe",
151
+ timeout: 5000,
152
+ });
153
+ } catch {}
154
+ }
155
+
156
+ // 2. 轮询 undici 等待 IP 信用恢复
157
+ const proxy = proxyUrl || DEFAULT_PROXY;
158
+ const agent = proxy ? new ProxyAgent(proxy) : null;
159
+ const POLL_MS = 5000;
160
+ const MAX_WAIT_MS = 3600_000;
161
+ const startTime = Date.now();
162
+
163
+ while (Date.now() - startTime < MAX_WAIT_MS) {
164
+ await new Promise((r) => setTimeout(r, POLL_MS));
165
+ try {
166
+ const { body } = await request(url, {
167
+ headers: PDP_HEADERS,
168
+ ...(agent ? { dispatcher: agent } : {}),
169
+ });
170
+ const html = await body.text();
171
+ if (
172
+ html.includes('"product_model"') ||
173
+ html.includes('"review_ratings"')
174
+ ) {
175
+ console.error("[shop-scraper] Captcha solved, IP credit restored");
176
+ return true;
177
+ }
178
+ } catch {}
179
+ const waited = Math.round((Date.now() - startTime) / 60000);
180
+ if (waited > 0 && waited % 5 === 0) {
181
+ console.error(
182
+ `[shop-scraper] 等待验证码解决中... 已等待 ${waited} 分钟`,
183
+ );
184
+ }
185
+ }
186
+
187
+ console.error("[shop-scraper] Windows: 等待验证码超时");
188
+ return false;
189
+ } finally {
190
+ winRecoveryRunning = false;
191
+ }
192
+ }
193
+
83
194
  /**
84
195
  * Open a headed browser for manual captcha solving, extract cookies
85
196
  * so undici can reuse them for a while.
@@ -90,6 +201,12 @@ function extractJsonByKey(html, keyPattern) {
90
201
  export async function resolveCaptchaWithBrowser(url, proxyUrl) {
91
202
  const { chromium } = await import("playwright");
92
203
 
204
+ // Windows 后台服务启动的浏览器在 Session 0,RDP 看不到。
205
+ // 用 start 命令推到当前交互桌面。
206
+ if (process.platform === "win32") {
207
+ return resolveCaptchaWindows(url, proxyUrl);
208
+ }
209
+
93
210
  const args = ["--no-sandbox"];
94
211
  if (proxyUrl) {
95
212
  args.push(`--proxy-server=${proxyUrl.replace("http://", "http://")}`);