tt-help-cli-ycl 1.3.65 → 1.3.73
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/attach.js +8 -4
- package/src/cli/refresh.js +16 -4
- package/src/lib/parse-ssr.mjs +1 -0
- package/src/scraper/explore-core.js +36 -19
- package/src/watch/data-store.js +390 -155
- package/src/watch/public/app.js +325 -62
- package/src/watch/public/index.html +10 -6
- package/src/watch/public/style.css +88 -1
- package/src/watch/server.js +73 -3
package/package.json
CHANGED
package/src/cli/attach.js
CHANGED
|
@@ -134,7 +134,9 @@ export async function handleAttach(options) {
|
|
|
134
134
|
`[Attach] 并行数: ${attachParallel}, 空闲间隔: ${attachInterval}秒, 服务端: ${serverUrl}${countryStr}`,
|
|
135
135
|
);
|
|
136
136
|
|
|
137
|
-
const scraper = new TikTokScraper(
|
|
137
|
+
const scraper = new TikTokScraper({
|
|
138
|
+
proxyServer: effectiveProxy || null,
|
|
139
|
+
});
|
|
138
140
|
const shutdown = async (signal) => {
|
|
139
141
|
if (shuttingDown) return;
|
|
140
142
|
shuttingDown = true;
|
|
@@ -287,12 +289,14 @@ export async function handleAttach(options) {
|
|
|
287
289
|
const task = successTasks.find(
|
|
288
290
|
(t) => t.uniqueId === r.uniqueId,
|
|
289
291
|
);
|
|
290
|
-
|
|
292
|
+
const info = task?.info;
|
|
293
|
+
const sellerTag = info?.ttSeller ? " [商家ttSeller=true]" : "";
|
|
294
|
+
if (info && info.error) {
|
|
291
295
|
attachLog(
|
|
292
|
-
`
|
|
296
|
+
` ⚠${sellerTag} @${r.uniqueId} 已记录 (statusCode=${info.statusCode})`,
|
|
293
297
|
);
|
|
294
298
|
} else {
|
|
295
|
-
attachLog(`
|
|
299
|
+
attachLog(` ✓${sellerTag} @${r.uniqueId} 已提交更新`);
|
|
296
300
|
}
|
|
297
301
|
} else {
|
|
298
302
|
failCount++;
|
package/src/cli/refresh.js
CHANGED
|
@@ -380,6 +380,7 @@ export async function handleRefresh(options) {
|
|
|
380
380
|
maxFollowing: exploreMaxFollowing || 100,
|
|
381
381
|
maxFollowers: exploreMaxFollowers || 100,
|
|
382
382
|
location: exploreLocation,
|
|
383
|
+
locationMode: "refresh",
|
|
383
384
|
browser,
|
|
384
385
|
proxyServer: cdpOptions.proxyServer || null,
|
|
385
386
|
},
|
|
@@ -405,6 +406,7 @@ export async function handleRefresh(options) {
|
|
|
405
406
|
maxFollowing: exploreMaxFollowing || 100,
|
|
406
407
|
maxFollowers: exploreMaxFollowers || 100,
|
|
407
408
|
location: exploreLocation,
|
|
409
|
+
locationMode: "refresh",
|
|
408
410
|
browser,
|
|
409
411
|
proxyServer: cdpOptions.proxyServer || null,
|
|
410
412
|
},
|
|
@@ -443,6 +445,7 @@ export async function handleRefresh(options) {
|
|
|
443
445
|
maxFollowing: exploreMaxFollowing || 100,
|
|
444
446
|
maxFollowers: exploreMaxFollowers || 100,
|
|
445
447
|
location: exploreLocation,
|
|
448
|
+
locationMode: "refresh",
|
|
446
449
|
browser,
|
|
447
450
|
proxyServer: cdpOptions.proxyServer || null,
|
|
448
451
|
},
|
|
@@ -525,10 +528,20 @@ export async function handleRefresh(options) {
|
|
|
525
528
|
|
|
526
529
|
processedCount++;
|
|
527
530
|
|
|
528
|
-
|
|
531
|
+
// refresh 模式:confirmedLocation 是二次确认的国家,写入 confirmed_location 列
|
|
532
|
+
// locationCreated 保持原始值不变
|
|
533
|
+
const refreshLocation =
|
|
534
|
+
result.confirmedLocation || result.locationCreated;
|
|
535
|
+
const guessedLocation = refreshLocation || null;
|
|
536
|
+
|
|
537
|
+
// 把 confirmedLocation 合并到 userInfo 中(通过 commitRedoJob 写入 DB)
|
|
538
|
+
const refreshUserInfo = { ...(result.userInfo || {}) };
|
|
539
|
+
if (result.confirmedLocation) {
|
|
540
|
+
refreshUserInfo.confirmedLocation = result.confirmedLocation;
|
|
541
|
+
}
|
|
529
542
|
|
|
530
543
|
const payload = {
|
|
531
|
-
userInfo:
|
|
544
|
+
userInfo: refreshUserInfo,
|
|
532
545
|
discoveredFollowing: (result.discoveredFollowing || []).map((f) => ({
|
|
533
546
|
handle: Array.isArray(f) ? f[0] : f,
|
|
534
547
|
displayName: Array.isArray(f) ? f[1] : null,
|
|
@@ -542,7 +555,6 @@ export async function handleRefresh(options) {
|
|
|
542
555
|
processed: result.processed,
|
|
543
556
|
hasFollowData: result.hasFollowData,
|
|
544
557
|
keepFollow: result.keepFollow,
|
|
545
|
-
locationCreated: result.locationCreated,
|
|
546
558
|
noVideo: result.noVideo,
|
|
547
559
|
collectedVideos: result.collectedVideos,
|
|
548
560
|
};
|
|
@@ -555,7 +567,7 @@ export async function handleRefresh(options) {
|
|
|
555
567
|
{
|
|
556
568
|
sourceUser: username,
|
|
557
569
|
videoList: result.videoList,
|
|
558
|
-
locationCreated:
|
|
570
|
+
locationCreated: refreshLocation,
|
|
559
571
|
ttSeller: result.userInfo?.ttSeller || false,
|
|
560
572
|
},
|
|
561
573
|
);
|
package/src/lib/parse-ssr.mjs
CHANGED
|
@@ -70,6 +70,7 @@ export function parseUserInfo(rawHtml) {
|
|
|
70
70
|
privateAccount: u.privateAccount,
|
|
71
71
|
language: u.language,
|
|
72
72
|
bio: u.signature || "",
|
|
73
|
+
bioLink: u.bioLink?.link || u.bioLink?.url || u.bioLink || null,
|
|
73
74
|
avatar: u.avatarLarger || u.avatarMedium || u.avatarThumb || "",
|
|
74
75
|
followerCount: s.followerCount,
|
|
75
76
|
followingCount: s.followingCount,
|
|
@@ -23,6 +23,7 @@ async function processExplore(page, username, options, log) {
|
|
|
23
23
|
maxFollowing = 50,
|
|
24
24
|
maxFollowers = 50,
|
|
25
25
|
location = DEFAULT_TARGET_LOCATIONS_CSV,
|
|
26
|
+
locationMode = "explore", // "explore" | "refresh"
|
|
26
27
|
proxyServer = null,
|
|
27
28
|
} = options;
|
|
28
29
|
|
|
@@ -91,10 +92,12 @@ async function processExplore(page, username, options, log) {
|
|
|
91
92
|
return result;
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
//
|
|
95
|
-
//
|
|
96
|
-
|
|
95
|
+
// 国家采样判断
|
|
96
|
+
// explore 模式:采样 5 个,优先命中目标国家,不匹配则回退众数 → 写 locationCreated
|
|
97
|
+
// refresh 模式:采样 7 个,纯众数逻辑 → 写 confirmedLocation(二次确认)
|
|
98
|
+
const SAMPLE_SIZE = locationMode === "refresh" ? 7 : 5;
|
|
97
99
|
let locationCreated = null;
|
|
100
|
+
let confirmedLocation = null;
|
|
98
101
|
let locationDecision = null;
|
|
99
102
|
const sampleVideos = videoArray.slice(0, SAMPLE_SIZE);
|
|
100
103
|
if (sampleVideos.length > 0) {
|
|
@@ -108,34 +111,48 @@ async function processExplore(page, username, options, log) {
|
|
|
108
111
|
` 国家采样(${locations.length}个): [${locations.filter(Boolean).join(", ") || "无数据"}]`,
|
|
109
112
|
);
|
|
110
113
|
const normalizedLocations = normalizeLocationList(locations, []);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
locationList,
|
|
114
|
-
);
|
|
114
|
+
|
|
115
|
+
// 统计频率
|
|
115
116
|
const freq = {};
|
|
116
117
|
for (const key of normalizedLocations) {
|
|
117
118
|
freq[key] = (freq[key] || 0) + 1;
|
|
118
119
|
}
|
|
119
120
|
const entries = Object.entries(freq).sort((a, b) => b[1] - a[1]);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
121
|
+
|
|
122
|
+
if (locationMode === "refresh") {
|
|
123
|
+
// refresh 模式:纯众数逻辑 → 写 confirmedLocation
|
|
124
|
+
if (entries.length > 0) {
|
|
125
|
+
confirmedLocation = entries[0][0];
|
|
126
|
+
locationDecision = `众数 (${entries[0][1]}次)`;
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
// explore 模式:优先命中目标国家,不匹配则回退众数
|
|
130
|
+
const matchedTargetLocation = findFirstMatchingLocation(
|
|
131
|
+
normalizedLocations,
|
|
132
|
+
locationList,
|
|
133
|
+
);
|
|
134
|
+
if (matchedTargetLocation) {
|
|
135
|
+
locationCreated = matchedTargetLocation;
|
|
136
|
+
locationDecision = "命中目标国家";
|
|
137
|
+
} else if (entries.length > 0) {
|
|
138
|
+
locationCreated = entries[0][0];
|
|
139
|
+
locationDecision = "回退众数";
|
|
140
|
+
}
|
|
126
141
|
}
|
|
127
142
|
}
|
|
128
143
|
|
|
129
144
|
result.locationCreated = locationCreated || null;
|
|
145
|
+
result.confirmedLocation = confirmedLocation || null;
|
|
130
146
|
log(
|
|
131
|
-
` 国家: ${result.locationCreated || "未知"}${locationDecision ? ` (${locationDecision})` : ""}`,
|
|
147
|
+
` 国家: ${result.confirmedLocation || result.locationCreated || "未知"}${locationDecision ? ` (${locationDecision})` : ""}`,
|
|
132
148
|
);
|
|
133
149
|
|
|
134
|
-
//
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
150
|
+
// 国家筛选:refresh 模式用 confirmedLocation,explore 模式用 locationCreated
|
|
151
|
+
const effectiveLocation =
|
|
152
|
+
locationMode === "refresh"
|
|
153
|
+
? result.confirmedLocation
|
|
154
|
+
: result.locationCreated;
|
|
155
|
+
const isTargetLocation = isLocationInList(effectiveLocation, locationList);
|
|
139
156
|
|
|
140
157
|
if (isTargetLocation) {
|
|
141
158
|
result.keepFollow = true;
|