tt-help-cli-ycl 1.4.49 → 1.4.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/explore.js +15 -18
- package/src/cli/refresh.js +13 -16
- package/src/lib/shop-scraper.mjs +0 -63
- package/src/scraper/explore-core.js +1 -4
package/package.json
CHANGED
package/src/cli/explore.js
CHANGED
|
@@ -388,7 +388,6 @@ export async function handleExplore(options) {
|
|
|
388
388
|
{
|
|
389
389
|
maxVideos: exploreMaxVideos,
|
|
390
390
|
enableFollow: exploreEnableFollow,
|
|
391
|
-
enableVideos: false,
|
|
392
391
|
loggedIn, // 传入登录状态,避免每次调用 isLoggedIn(page)
|
|
393
392
|
maxFollowing: exploreMaxFollowing,
|
|
394
393
|
maxFollowers: exploreMaxFollowers,
|
|
@@ -416,7 +415,6 @@ export async function handleExplore(options) {
|
|
|
416
415
|
maxVideos: exploreMaxVideos,
|
|
417
416
|
enableFollow: exploreEnableFollow,
|
|
418
417
|
loggedIn, // 传入登录状态
|
|
419
|
-
enableVideos: false,
|
|
420
418
|
maxFollowing: exploreMaxFollowing,
|
|
421
419
|
maxFollowers: exploreMaxFollowers,
|
|
422
420
|
location: exploreLocation,
|
|
@@ -465,7 +463,6 @@ export async function handleExplore(options) {
|
|
|
465
463
|
maxVideos: exploreMaxVideos,
|
|
466
464
|
enableFollow: exploreEnableFollow,
|
|
467
465
|
loggedIn,
|
|
468
|
-
enableVideos: false,
|
|
469
466
|
maxFollowing: exploreMaxFollowing,
|
|
470
467
|
maxFollowers: exploreMaxFollowers,
|
|
471
468
|
location: exploreLocation,
|
|
@@ -588,21 +585,21 @@ export async function handleExplore(options) {
|
|
|
588
585
|
};
|
|
589
586
|
await apiPost(`${serverUrl}/api/job/${username}`, payload);
|
|
590
587
|
|
|
591
|
-
//
|
|
592
|
-
if (result.videoList && result.videoList.length > 0) {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
}
|
|
588
|
+
// 视频登记(暂时关闭)
|
|
589
|
+
// if (result.videoList && result.videoList.length > 0) {
|
|
590
|
+
// const { registered, skipped } = await apiPost(
|
|
591
|
+
// `${serverUrl}/api/videos`,
|
|
592
|
+
// {
|
|
593
|
+
// sourceUser: username,
|
|
594
|
+
// videoList: result.videoList,
|
|
595
|
+
// locationCreated: result.locationCreated,
|
|
596
|
+
// ttSeller: result.userInfo?.ttSeller || false,
|
|
597
|
+
// },
|
|
598
|
+
// );
|
|
599
|
+
// console.error(
|
|
600
|
+
// ` 视频登记: ${registered} 条新增, ${skipped} 条已存在`,
|
|
601
|
+
// );
|
|
602
|
+
// }
|
|
606
603
|
|
|
607
604
|
console.error(" 已提交");
|
|
608
605
|
|
package/src/cli/refresh.js
CHANGED
|
@@ -398,7 +398,6 @@ export async function handleRefresh(options) {
|
|
|
398
398
|
{
|
|
399
399
|
maxVideos: exploreMaxVideos || 16,
|
|
400
400
|
enableFollow: exploreEnableFollow !== false,
|
|
401
|
-
enableVideos: false,
|
|
402
401
|
loggedIn,
|
|
403
402
|
maxFollowing: exploreMaxFollowing || 100,
|
|
404
403
|
maxFollowers: exploreMaxFollowers || 100,
|
|
@@ -426,7 +425,6 @@ export async function handleRefresh(options) {
|
|
|
426
425
|
{
|
|
427
426
|
maxVideos: exploreMaxVideos || 16,
|
|
428
427
|
enableFollow: exploreEnableFollow !== false,
|
|
429
|
-
enableVideos: false,
|
|
430
428
|
loggedIn,
|
|
431
429
|
maxFollowing: exploreMaxFollowing || 100,
|
|
432
430
|
maxFollowers: exploreMaxFollowers || 100,
|
|
@@ -476,7 +474,6 @@ export async function handleRefresh(options) {
|
|
|
476
474
|
{
|
|
477
475
|
maxVideos: exploreMaxVideos || 16,
|
|
478
476
|
enableFollow: exploreEnableFollow !== false,
|
|
479
|
-
enableVideos: false,
|
|
480
477
|
loggedIn,
|
|
481
478
|
maxFollowing: exploreMaxFollowing || 100,
|
|
482
479
|
maxFollowers: exploreMaxFollowers || 100,
|
|
@@ -608,19 +605,19 @@ export async function handleRefresh(options) {
|
|
|
608
605
|
};
|
|
609
606
|
await apiPost(`${serverUrl}/api/redo-job/${username}`, payload);
|
|
610
607
|
|
|
611
|
-
//
|
|
612
|
-
if (result.videoList && result.videoList.length > 0) {
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
}
|
|
608
|
+
// 视频登记(暂时关闭)
|
|
609
|
+
// if (result.videoList && result.videoList.length > 0) {
|
|
610
|
+
// const { registered, skipped } = await apiPost(
|
|
611
|
+
// `${serverUrl}/api/videos`,
|
|
612
|
+
// {
|
|
613
|
+
// sourceUser: username,
|
|
614
|
+
// videoList: result.videoList,
|
|
615
|
+
// locationCreated: result.locationCreated,
|
|
616
|
+
// ttSeller: result.userInfo?.ttSeller || false,
|
|
617
|
+
// },
|
|
618
|
+
// );
|
|
619
|
+
// console.error(` 视频登记: ${registered} 条新增, ${skipped} 条已存在`);
|
|
620
|
+
// }
|
|
624
621
|
|
|
625
622
|
console.error(" 已提交");
|
|
626
623
|
|
package/src/lib/shop-scraper.mjs
CHANGED
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { ProxyAgent, request } from "undici";
|
|
20
|
-
import { execSync } from "child_process";
|
|
21
20
|
import { DEFAULT_PROXY } from "./constants.js";
|
|
22
21
|
|
|
23
22
|
const PDP_HEADERS = {
|
|
@@ -81,62 +80,6 @@ function extractJsonByKey(html, keyPattern) {
|
|
|
81
80
|
return null;
|
|
82
81
|
}
|
|
83
82
|
|
|
84
|
-
/**
|
|
85
|
-
* Windows: 后台服务的 Playwright 浏览器在 Session 0(隔离桌面),
|
|
86
|
-
* RDP 看不到。改为用 start 命令推到交互桌面,然后轮询 undici 等 IP 恢复信用。
|
|
87
|
-
*/
|
|
88
|
-
async function resolveCaptchaWindows(url, proxyUrl) {
|
|
89
|
-
// 用 Windows start 命令在交互桌面打开 Edge
|
|
90
|
-
const edgeUrl = url.replace("shop.tiktok.com", "www.tiktok.com/shop/pdp");
|
|
91
|
-
try {
|
|
92
|
-
execSync(`start "" "microsoft-edge:${edgeUrl}"`, {
|
|
93
|
-
shell: "cmd.exe",
|
|
94
|
-
timeout: 5000,
|
|
95
|
-
});
|
|
96
|
-
console.error(
|
|
97
|
-
"[shop-scraper] Windows: 已用 start 命令在交互桌面打开 Edge,请在 RDP 中完成验证码",
|
|
98
|
-
);
|
|
99
|
-
} catch (e) {
|
|
100
|
-
console.error("[shop-scraper] Windows: start 命令失败:", e.message);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// 轮询 undici 等待 IP 信用恢复(用户解完验证码后代理 IP 就有信用了)
|
|
104
|
-
const proxy = proxyUrl || DEFAULT_PROXY;
|
|
105
|
-
const agent = proxy ? new ProxyAgent(proxy) : null;
|
|
106
|
-
const POLL_MS = 5000;
|
|
107
|
-
const MAX_WAIT_MS = 3600_000; // 最长等 1 小时
|
|
108
|
-
const start = Date.now();
|
|
109
|
-
|
|
110
|
-
while (Date.now() - start < MAX_WAIT_MS) {
|
|
111
|
-
await new Promise((r) => setTimeout(r, POLL_MS));
|
|
112
|
-
try {
|
|
113
|
-
const { body } = await request(url, {
|
|
114
|
-
headers: PDP_HEADERS,
|
|
115
|
-
...(agent ? { dispatcher: agent } : {}),
|
|
116
|
-
});
|
|
117
|
-
const html = await body.text();
|
|
118
|
-
if (
|
|
119
|
-
html.includes('"product_model"') ||
|
|
120
|
-
html.includes('"review_ratings"')
|
|
121
|
-
) {
|
|
122
|
-
console.error("[shop-scraper] Captcha solved, IP credit restored");
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
} catch {
|
|
126
|
-
// 继续轮询
|
|
127
|
-
}
|
|
128
|
-
// 每 60 秒打印一次状态
|
|
129
|
-
if (Math.round((Date.now() - start) / 60000) % 5 === 0) {
|
|
130
|
-
console.error(
|
|
131
|
-
`[shop-scraper] 等待验证码解决中... 已等待 ${Math.round((Date.now() - start) / 60000)} 分钟`,
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
console.error("[shop-scraper] Windows: 等待验证码超时");
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
83
|
/**
|
|
141
84
|
* Open a headed browser for manual captcha solving, extract cookies
|
|
142
85
|
* so undici can reuse them for a while.
|
|
@@ -147,12 +90,6 @@ async function resolveCaptchaWindows(url, proxyUrl) {
|
|
|
147
90
|
export async function resolveCaptchaWithBrowser(url, proxyUrl) {
|
|
148
91
|
const { chromium } = await import("playwright");
|
|
149
92
|
|
|
150
|
-
// Windows 后台服务启动的浏览器在 Session 0,RDP 看不到。
|
|
151
|
-
// 用 start 命令推到当前交互桌面。
|
|
152
|
-
if (process.platform === "win32") {
|
|
153
|
-
return resolveCaptchaWindows(url, proxyUrl);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
93
|
const args = ["--no-sandbox"];
|
|
157
94
|
if (proxyUrl) {
|
|
158
95
|
args.push(`--proxy-server=${proxyUrl.replace("http://", "http://")}`);
|
|
@@ -60,10 +60,7 @@ async function processExplore(page, username, options, log) {
|
|
|
60
60
|
|
|
61
61
|
try {
|
|
62
62
|
log(` 访问 @${username} 主页...`);
|
|
63
|
-
const videoList =
|
|
64
|
-
options.enableVideos !== false
|
|
65
|
-
? await collectVideos(page, username, maxVideos, log)
|
|
66
|
-
: null;
|
|
63
|
+
const videoList = await collectVideos(page, username, maxVideos, log);
|
|
67
64
|
|
|
68
65
|
log(" 获取用户信息...");
|
|
69
66
|
const info = await getUserInfo(page);
|