tt-help-cli-ycl 1.3.11 → 1.3.13
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/README.md +17 -17
- package/cli.js +9 -9
- package/package.json +45 -46
- package/{bat → scripts}/run-explore.bat +68 -68
- package/{bat → scripts}/run-explore.ps1 +81 -81
- package/{bat → scripts}/run-explore.sh +73 -73
- package/scripts/test-captcha-lib.mjs +68 -0
- package/scripts/test-captcha.mjs +81 -0
- package/scripts/test-incognito-lib.mjs +36 -0
- package/scripts/test-login-state.mjs +128 -0
- package/scripts/test-safe-click.mjs +45 -0
- package/src/cli/auto.js +186 -157
- package/src/cli/config.js +116 -0
- package/src/cli/explore-default.js +83 -0
- package/src/cli/explore.js +227 -181
- package/src/cli/progress.js +111 -111
- package/src/cli/refresh.js +216 -0
- package/src/cli/scrape.js +47 -47
- package/src/cli/utils.js +18 -18
- package/src/cli/videos.js +41 -41
- package/src/cli/watch.js +31 -31
- package/src/lib/args.js +456 -391
- package/src/lib/browser/anti-detect.js +23 -23
- package/src/lib/browser/cdp.js +194 -142
- package/src/lib/browser/launch.js +43 -43
- package/src/lib/browser/page.js +146 -87
- package/src/lib/constants.js +119 -119
- package/src/lib/delay.js +54 -54
- package/src/lib/explore-fetch.js +118 -118
- package/src/lib/fetcher.js +45 -45
- package/src/lib/filter.js +66 -66
- package/src/lib/io.js +54 -54
- package/src/lib/output.js +80 -80
- package/src/{scraper/modules/page-error-detector.mjs → lib/page-error-detector.js} +70 -70
- package/src/lib/parser.js +47 -47
- package/src/lib/retry.js +45 -45
- package/src/lib/scrape.js +40 -40
- package/src/{scraper/modules/scroll-collector.mjs → lib/scroll-collector.js} +231 -189
- package/src/lib/url.js +52 -52
- package/src/main.js +48 -0
- package/src/results/user-videos-bar.lar.lar.moeta.json +37 -0
- package/src/scraper/{auto-core.mjs → auto-core.js} +203 -194
- package/src/scraper/{core.mjs → core.js} +211 -190
- package/src/scraper/{explore-core.mjs → explore-core.js} +180 -171
- package/src/scraper/modules/{captcha-handler.mjs → captcha-handler.js} +114 -114
- package/src/scraper/modules/{comment-extractor.mjs → comment-extractor.js} +74 -69
- package/src/scraper/modules/{follow-extractor.mjs → follow-extractor.js} +121 -121
- package/src/scraper/modules/{guess-extractor.mjs → guess-extractor.js} +51 -51
- package/src/scraper/modules/page-error-detector.js +1 -0
- package/src/scraper/modules/{page-helpers.mjs → page-helpers.js} +48 -48
- package/src/scraper/modules/scroll-collector.js +8 -0
- package/src/scraper/refresh-core.js +179 -0
- package/src/videos/{core.mjs → core.js} +126 -126
- package/src/watch/data-store.js +431 -0
- package/src/watch/public/index.html +721 -690
- package/src/watch/{server.mjs → server.js} +484 -349
- package/src/main.mjs +0 -234
- package/src/test-auto-follow.cjs +0 -109
- package/src/test-extractors.cjs +0 -75
- package/src/test-follow.cjs +0 -41
- package/src/watch/data-store.mjs +0 -274
|
@@ -1,189 +1,231 @@
|
|
|
1
|
-
import { delay } from
|
|
2
|
-
import { detectPageError } from
|
|
3
|
-
|
|
4
|
-
async function doCollect(
|
|
5
|
-
page,
|
|
6
|
-
{ container, findScrollable, fnStr, extraArgs },
|
|
7
|
-
) {
|
|
8
|
-
return page.evaluate(
|
|
9
|
-
({ fn: fnStr, containerSelector, findScrollableFlag, args }) => {
|
|
10
|
-
let el;
|
|
11
|
-
if (!containerSelector) {
|
|
12
|
-
el = window;
|
|
13
|
-
} else {
|
|
14
|
-
el = document.querySelector(containerSelector);
|
|
15
|
-
if (!el) {
|
|
16
|
-
el = window;
|
|
17
|
-
} else if (findScrollableFlag) {
|
|
18
|
-
let current = el;
|
|
19
|
-
let found = false;
|
|
20
|
-
while (current && current !== document.body) {
|
|
21
|
-
if (current.scrollHeight > current.clientHeight + 10) {
|
|
22
|
-
el = current;
|
|
23
|
-
found = true;
|
|
24
|
-
break;
|
|
25
|
-
}
|
|
26
|
-
current = current.parentElement;
|
|
27
|
-
}
|
|
28
|
-
if (!found) el = document.body;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
'[class*="
|
|
45
|
-
'[class*="
|
|
46
|
-
'[
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
newItems
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
1
|
+
import { delay } from './delay.js';
|
|
2
|
+
import { detectPageError } from './page-error-detector.js';
|
|
3
|
+
|
|
4
|
+
async function doCollect(
|
|
5
|
+
page,
|
|
6
|
+
{ container, findScrollable, fnStr, extraArgs },
|
|
7
|
+
) {
|
|
8
|
+
return page.evaluate(
|
|
9
|
+
({ fn: fnStr, containerSelector, findScrollableFlag, args }) => {
|
|
10
|
+
let el;
|
|
11
|
+
if (!containerSelector) {
|
|
12
|
+
el = window;
|
|
13
|
+
} else {
|
|
14
|
+
el = document.querySelector(containerSelector);
|
|
15
|
+
if (!el) {
|
|
16
|
+
el = window;
|
|
17
|
+
} else if (findScrollableFlag) {
|
|
18
|
+
let current = el;
|
|
19
|
+
let found = false;
|
|
20
|
+
while (current && current !== document.body) {
|
|
21
|
+
if (current.scrollHeight > current.clientHeight + 10) {
|
|
22
|
+
el = current;
|
|
23
|
+
found = true;
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
current = current.parentElement;
|
|
27
|
+
}
|
|
28
|
+
if (!found) el = document.body;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const fn = eval("(" + fnStr + ")");
|
|
33
|
+
return fn(el, args);
|
|
34
|
+
},
|
|
35
|
+
{ fn: fnStr, containerSelector: container, findScrollableFlag: findScrollable, args: extraArgs },
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const LOADING_SELECTORS = [
|
|
40
|
+
'[class*="loading"]',
|
|
41
|
+
'[class*="Loading"]',
|
|
42
|
+
'[class*="spinner"]',
|
|
43
|
+
'[class*="Spinner"]',
|
|
44
|
+
'[class*="skeleton"]',
|
|
45
|
+
'[class*="Skeleton"]',
|
|
46
|
+
'[aria-busy="true"]',
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
async function waitForLoading(page) {
|
|
50
|
+
const maxWait = 5000;
|
|
51
|
+
const startTime = Date.now();
|
|
52
|
+
while (Date.now() - startTime < maxWait) {
|
|
53
|
+
const isLoading = await page.evaluate((sels) => {
|
|
54
|
+
if (document.readyState !== "complete") return true;
|
|
55
|
+
for (const sel of sels) {
|
|
56
|
+
const el = document.querySelector(sel);
|
|
57
|
+
if (el && el.offsetParent !== null) return true;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}, LOADING_SELECTORS);
|
|
61
|
+
if (!isLoading) return;
|
|
62
|
+
await delay(300, 600);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function collectWithoutScroll(page, options) {
|
|
67
|
+
const {
|
|
68
|
+
container,
|
|
69
|
+
findScrollable = false,
|
|
70
|
+
collectFn,
|
|
71
|
+
extraArgs,
|
|
72
|
+
} = options;
|
|
73
|
+
|
|
74
|
+
if (!collectFn) throw new Error("collectFn is required");
|
|
75
|
+
|
|
76
|
+
const fnStr =
|
|
77
|
+
typeof collectFn === "function" ? collectFn.toString() : collectFn;
|
|
78
|
+
|
|
79
|
+
const result = await doCollect(page, {
|
|
80
|
+
container,
|
|
81
|
+
findScrollable,
|
|
82
|
+
fnStr,
|
|
83
|
+
extraArgs,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return result.items || [];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function scrollToBottom(page, options = {}) {
|
|
90
|
+
const {
|
|
91
|
+
container,
|
|
92
|
+
findScrollable = false,
|
|
93
|
+
delayRange = [800, 1500],
|
|
94
|
+
maxScroll = 200,
|
|
95
|
+
} = options;
|
|
96
|
+
|
|
97
|
+
for (let i = 0; i < maxScroll; i++) {
|
|
98
|
+
await threePhaseScroll(page, { container, findScrollable });
|
|
99
|
+
await delay(delayRange[0], delayRange[1]);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function scrollAndCollect(page, options) {
|
|
104
|
+
const {
|
|
105
|
+
container,
|
|
106
|
+
findScrollable = false,
|
|
107
|
+
collectFn,
|
|
108
|
+
extraArgs,
|
|
109
|
+
delayRange = [800, 1500],
|
|
110
|
+
maxItems,
|
|
111
|
+
maxRounds = 200,
|
|
112
|
+
staleThreshold = 3,
|
|
113
|
+
uniqueKey,
|
|
114
|
+
onRound,
|
|
115
|
+
} = options;
|
|
116
|
+
|
|
117
|
+
if (!collectFn) throw new Error("collectFn is required");
|
|
118
|
+
|
|
119
|
+
const fnStr =
|
|
120
|
+
typeof collectFn === "function" ? collectFn.toString() : collectFn;
|
|
121
|
+
const allItems = [];
|
|
122
|
+
const seenKeys = uniqueKey ? new Set() : null;
|
|
123
|
+
let staleCount = 0;
|
|
124
|
+
|
|
125
|
+
const processItems = (result) => {
|
|
126
|
+
const raw = result.items || [];
|
|
127
|
+
const newItems = uniqueKey
|
|
128
|
+
? raw.filter((item) => {
|
|
129
|
+
const key = uniqueKey(item);
|
|
130
|
+
if (seenKeys.has(key)) return false;
|
|
131
|
+
seenKeys.add(key);
|
|
132
|
+
return true;
|
|
133
|
+
})
|
|
134
|
+
: raw;
|
|
135
|
+
allItems.push(...newItems);
|
|
136
|
+
return newItems;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const isDone = (newItems) => {
|
|
140
|
+
if (maxItems !== undefined && allItems.length >= maxItems) return true;
|
|
141
|
+
if (newItems.length === 0) {
|
|
142
|
+
staleCount++;
|
|
143
|
+
if (staleCount >= staleThreshold) return true;
|
|
144
|
+
} else {
|
|
145
|
+
staleCount = 0;
|
|
146
|
+
}
|
|
147
|
+
return false;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const collectCtx = { container, findScrollable, fnStr, extraArgs };
|
|
151
|
+
|
|
152
|
+
const pageError = await detectPageError(page);
|
|
153
|
+
|
|
154
|
+
if (pageError) return [];
|
|
155
|
+
|
|
156
|
+
await waitForLoading(page);
|
|
157
|
+
let result = await doCollect(page, collectCtx);
|
|
158
|
+
let newItems = processItems(result);
|
|
159
|
+
if (onRound) onRound(0, newItems, allItems);
|
|
160
|
+
if (isDone(newItems)) return allItems;
|
|
161
|
+
|
|
162
|
+
for (let round = 1; round < maxRounds; round++) {
|
|
163
|
+
await threePhaseScroll(page, { container, findScrollable });
|
|
164
|
+
await delay(delayRange[0], delayRange[1]);
|
|
165
|
+
await waitForLoading(page);
|
|
166
|
+
|
|
167
|
+
result = await doCollect(page, collectCtx);
|
|
168
|
+
newItems = processItems(result);
|
|
169
|
+
|
|
170
|
+
if (onRound) onRound(round, newItems, allItems);
|
|
171
|
+
|
|
172
|
+
if (isDone(newItems)) break;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return allItems;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function threePhaseScroll(page, { container, findScrollable }) {
|
|
179
|
+
await page.evaluate(
|
|
180
|
+
async (opts) => {
|
|
181
|
+
let el;
|
|
182
|
+
if (!opts.container) {
|
|
183
|
+
el = window;
|
|
184
|
+
} else {
|
|
185
|
+
el = document.querySelector(opts.container);
|
|
186
|
+
if (!el) {
|
|
187
|
+
el = window;
|
|
188
|
+
} else if (opts.findScrollable) {
|
|
189
|
+
let current = el;
|
|
190
|
+
let found = false;
|
|
191
|
+
while (current && current !== document.body) {
|
|
192
|
+
if (current.scrollHeight > current.clientHeight + 10) {
|
|
193
|
+
el = current;
|
|
194
|
+
found = true;
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
current = current.parentElement;
|
|
198
|
+
}
|
|
199
|
+
if (!found) el = document.body;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const randDelay = (min, max) =>
|
|
204
|
+
new Promise((r) => setTimeout(r, min + Math.random() * (max - min)));
|
|
205
|
+
|
|
206
|
+
if (el === window) {
|
|
207
|
+
window.scrollBy(0, window.innerHeight);
|
|
208
|
+
await randDelay(400, 800);
|
|
209
|
+
window.scrollBy(0, -200);
|
|
210
|
+
await randDelay(200, 400);
|
|
211
|
+
window.scrollBy(0, window.innerHeight);
|
|
212
|
+
} else {
|
|
213
|
+
el.scrollTop = el.scrollHeight;
|
|
214
|
+
await randDelay(400, 800);
|
|
215
|
+
el.scrollTop -= 100 + Math.random() * 100;
|
|
216
|
+
await randDelay(200, 400);
|
|
217
|
+
el.scrollTop = el.scrollHeight;
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
{ container, findScrollable },
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export {
|
|
225
|
+
scrollAndCollect,
|
|
226
|
+
scrollToBottom,
|
|
227
|
+
collectWithoutScroll,
|
|
228
|
+
doCollect,
|
|
229
|
+
waitForLoading,
|
|
230
|
+
threePhaseScroll,
|
|
231
|
+
};
|
package/src/lib/url.js
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
const BASE_URL = 'https://www.tiktok.com';
|
|
2
|
-
|
|
3
|
-
export function extractUniqueId(url) {
|
|
4
|
-
const m = url.match(/\/@([^/]+)/);
|
|
5
|
-
return m ? m[1] : null;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function extractVideoId(url) {
|
|
9
|
-
const m = url.match(/\/video\/(\d+)/);
|
|
10
|
-
return m ? m[1] : null;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export function normalizeUsername(input) {
|
|
14
|
-
return (input || '').replace(/^@/, '');
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function toProfileUrl(handle) {
|
|
18
|
-
const clean = normalizeUsername(handle);
|
|
19
|
-
return `${BASE_URL}/@${clean}`;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function toVideoUrl(handle, videoId) {
|
|
23
|
-
const clean = normalizeUsername(handle);
|
|
24
|
-
return `${BASE_URL}/@${clean}/video/${videoId}`;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function ensureAbsoluteUrl(href) {
|
|
28
|
-
if (href.startsWith('http')) return href;
|
|
29
|
-
return `${BASE_URL}${href}`;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function isProfileUrl(url) {
|
|
33
|
-
return /\/@[\w-]+(?:$|[?#])/.test(url);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function isVideoUrl(url) {
|
|
37
|
-
return /\/video\/\d+/.test(url);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function extractDisplayPath(url) {
|
|
41
|
-
try {
|
|
42
|
-
const parts = new URL(url).pathname.split('/').filter(Boolean);
|
|
43
|
-
return parts.slice(-2).join('/');
|
|
44
|
-
} catch {
|
|
45
|
-
return url;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function extractAuthorFromVideoUrl(url) {
|
|
50
|
-
const m = url.match(/@([^/]+)\/video/);
|
|
51
|
-
return m ? '@' + m[1] : null;
|
|
52
|
-
}
|
|
1
|
+
const BASE_URL = 'https://www.tiktok.com';
|
|
2
|
+
|
|
3
|
+
export function extractUniqueId(url) {
|
|
4
|
+
const m = url.match(/\/@([^/]+)/);
|
|
5
|
+
return m ? m[1] : null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function extractVideoId(url) {
|
|
9
|
+
const m = url.match(/\/video\/(\d+)/);
|
|
10
|
+
return m ? m[1] : null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function normalizeUsername(input) {
|
|
14
|
+
return (input || '').replace(/^@/, '');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function toProfileUrl(handle) {
|
|
18
|
+
const clean = normalizeUsername(handle);
|
|
19
|
+
return `${BASE_URL}/@${clean}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function toVideoUrl(handle, videoId) {
|
|
23
|
+
const clean = normalizeUsername(handle);
|
|
24
|
+
return `${BASE_URL}/@${clean}/video/${videoId}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function ensureAbsoluteUrl(href) {
|
|
28
|
+
if (href.startsWith('http')) return href;
|
|
29
|
+
return `${BASE_URL}${href}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function isProfileUrl(url) {
|
|
33
|
+
return /\/@[\w-]+(?:$|[?#])/.test(url);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function isVideoUrl(url) {
|
|
37
|
+
return /\/video\/\d+/.test(url);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function extractDisplayPath(url) {
|
|
41
|
+
try {
|
|
42
|
+
const parts = new URL(url).pathname.split('/').filter(Boolean);
|
|
43
|
+
return parts.slice(-2).join('/');
|
|
44
|
+
} catch {
|
|
45
|
+
return url;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function extractAuthorFromVideoUrl(url) {
|
|
50
|
+
const m = url.match(/@([^/]+)\/video/);
|
|
51
|
+
return m ? '@' + m[1] : null;
|
|
52
|
+
}
|
package/src/main.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { parseArgs } from './lib/args.js';
|
|
2
|
+
import { proxy, HELP_TEXT, getConfigText } from './lib/constants.js';
|
|
3
|
+
import { parseFilter } from './lib/filter.js';
|
|
4
|
+
import { handleScrape } from './cli/scrape.js';
|
|
5
|
+
import { handleVideos } from './cli/videos.js';
|
|
6
|
+
import { handleAuto } from './cli/auto.js';
|
|
7
|
+
import { handleExplore } from './cli/explore.js';
|
|
8
|
+
import { handleRefresh } from './cli/refresh.js';
|
|
9
|
+
import { handleWatch } from './cli/watch.js';
|
|
10
|
+
import { handleConfig, showConfig, showUsage, version } from './cli/config.js';
|
|
11
|
+
import { runExploreDefault, runScrapeDefault } from './cli/explore-default.js';
|
|
12
|
+
|
|
13
|
+
async function main() {
|
|
14
|
+
const parsed = parseArgs();
|
|
15
|
+
|
|
16
|
+
switch (parsed.subcommand) {
|
|
17
|
+
case 'scrape': return handleScrape(parsed);
|
|
18
|
+
case 'videos': return handleVideos(parsed);
|
|
19
|
+
case 'auto': return handleAuto(parsed);
|
|
20
|
+
case 'explore':return handleExplore(parsed);
|
|
21
|
+
case 'refresh':return handleRefresh(parsed);
|
|
22
|
+
case 'watch': return handleWatch(parsed);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const { urls, outputFile, outputFormat, exploreCount, showConfig: showCfg, showHelp, showVersion, customProxy, configAction, configKey, configValue, pipeMode, filterStr } = parsed;
|
|
26
|
+
const proxyUrl = customProxy || proxy;
|
|
27
|
+
const filter = parseFilter(filterStr);
|
|
28
|
+
|
|
29
|
+
if (showVersion) {
|
|
30
|
+
console.log(version);
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
if (showHelp) return showUsage();
|
|
34
|
+
if (configAction) return handleConfig(configAction, configKey, configValue);
|
|
35
|
+
if (showCfg) return showConfig(urls, outputFile);
|
|
36
|
+
if (urls.length === 0 && exploreCount === 0) return showUsage();
|
|
37
|
+
|
|
38
|
+
if (exploreCount > 0) {
|
|
39
|
+
await runExploreDefault(exploreCount, urls, proxyUrl, outputFile, outputFormat, pipeMode, filter);
|
|
40
|
+
} else {
|
|
41
|
+
await runScrapeDefault(urls, proxyUrl, outputFile, outputFormat, filter);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
main().catch(err => {
|
|
46
|
+
console.error(`错误: ${err.message}`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"user": {
|
|
3
|
+
"uniqueId": "bar.lar.lar.moeta",
|
|
4
|
+
"secUid": "MS4wLjABAAAA3cgKTWvKfga0JAWeakAzx3zQ-aFAC8RuQvxD4HQFraKKsc_TbOIyMo3_ofVlXofV",
|
|
5
|
+
"nickname": "Bar Lar Lar Moetain",
|
|
6
|
+
"ttSeller": false,
|
|
7
|
+
"verified": false,
|
|
8
|
+
"followerCount": 24000,
|
|
9
|
+
"videoCount": 749,
|
|
10
|
+
"followingCount": 4293,
|
|
11
|
+
"heartCount": 254300,
|
|
12
|
+
"signature": ""
|
|
13
|
+
},
|
|
14
|
+
"totalVideos": 5,
|
|
15
|
+
"videos": [
|
|
16
|
+
{
|
|
17
|
+
"id": "7638231799084158228",
|
|
18
|
+
"url": "https://www.tiktok.com/@bar.lar.lar.moeta/video/7638231799084158228"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "7638162444698914068",
|
|
22
|
+
"url": "https://www.tiktok.com/@bar.lar.lar.moeta/video/7638162444698914068"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"id": "7638116251767819541",
|
|
26
|
+
"url": "https://www.tiktok.com/@bar.lar.lar.moeta/video/7638116251767819541"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "7638069637321690388",
|
|
30
|
+
"url": "https://www.tiktok.com/@bar.lar.lar.moeta/video/7638069637321690388"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": "7637927171025112341",
|
|
34
|
+
"url": "https://www.tiktok.com/@bar.lar.lar.moeta/video/7637927171025112341"
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|