jav-manager 0.1.0 → 0.1.2
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/dist/data/curlImpersonateFetcher.js +29 -232
- package/dist/data/javdb.js +2 -104
- package/dist/jav_cache.json +1 -0
- package/dist/utils/curlBinaryResolver.js +103 -25
- package/package.json +4 -1
- package/scripts/postinstall.js +31 -0
- package/third_party/curl-impersonate/bin/curl-impersonate +0 -0
- package/third_party/curl-impersonate/bin/curl_chrome100 +33 -0
- package/third_party/curl-impersonate/bin/curl_chrome101 +33 -0
- package/third_party/curl-impersonate/bin/curl_chrome104 +33 -0
- package/third_party/curl-impersonate/bin/curl_chrome107 +33 -0
- package/third_party/curl-impersonate/bin/curl_chrome110 +33 -0
- package/third_party/curl-impersonate/bin/curl_chrome116 +33 -0
- package/third_party/curl-impersonate/bin/curl_chrome119 +34 -0
- package/third_party/curl-impersonate/bin/curl_chrome120 +34 -0
- package/third_party/curl-impersonate/bin/curl_chrome123 +37 -0
- package/third_party/curl-impersonate/bin/curl_chrome124 +40 -0
- package/third_party/curl-impersonate/bin/curl_chrome131 +39 -0
- package/third_party/curl-impersonate/bin/curl_chrome131_android +40 -0
- package/third_party/curl-impersonate/bin/curl_chrome133a +40 -0
- package/third_party/curl-impersonate/bin/curl_chrome136 +37 -0
- package/third_party/curl-impersonate/bin/curl_chrome142 +6 -0
- package/third_party/curl-impersonate/bin/curl_chrome99 +33 -0
- package/third_party/curl-impersonate/bin/curl_chrome99_android +33 -0
- package/third_party/curl-impersonate/bin/curl_edge101 +33 -0
- package/third_party/curl-impersonate/bin/curl_edge99 +33 -0
- package/third_party/curl-impersonate/bin/curl_firefox133 +36 -0
- package/third_party/curl-impersonate/bin/curl_firefox135 +37 -0
- package/third_party/curl-impersonate/bin/curl_firefox144 +6 -0
- package/third_party/curl-impersonate/bin/curl_safari153 +27 -0
- package/third_party/curl-impersonate/bin/curl_safari155 +28 -0
- package/third_party/curl-impersonate/bin/curl_safari170 +31 -0
- package/third_party/curl-impersonate/bin/curl_safari172_ios +31 -0
- package/third_party/curl-impersonate/bin/curl_safari180 +32 -0
- package/third_party/curl-impersonate/bin/curl_safari180_ios +32 -0
- package/third_party/curl-impersonate/bin/curl_safari184 +32 -0
- package/third_party/curl-impersonate/bin/curl_safari184_ios +32 -0
- package/third_party/curl-impersonate/bin/curl_safari260 +39 -0
- package/third_party/curl-impersonate/bin/curl_safari2601 +6 -0
- package/third_party/curl-impersonate/bin/curl_safari260_ios +37 -0
- package/third_party/curl-impersonate/bin/curl_tor145 +38 -0
|
@@ -69,14 +69,16 @@ function stripCacertArgs(args) {
|
|
|
69
69
|
return result;
|
|
70
70
|
}
|
|
71
71
|
/**
|
|
72
|
-
* HTTP fetcher
|
|
73
|
-
*
|
|
72
|
+
* HTTP fetcher using curl-impersonate to bypass Cloudflare protection.
|
|
73
|
+
* curl-impersonate is the only supported request method.
|
|
74
74
|
*/
|
|
75
75
|
class CurlImpersonateFetcher {
|
|
76
76
|
config;
|
|
77
77
|
available = null;
|
|
78
78
|
binaryPath = null;
|
|
79
|
-
|
|
79
|
+
/** When true the resolved binary is the main `curl-impersonate` executable
|
|
80
|
+
* and we must pass `--impersonate <target>` to select the TLS profile. */
|
|
81
|
+
useImpersonateFlag = false;
|
|
80
82
|
cookieJarFiles = new Map();
|
|
81
83
|
constructor(config) {
|
|
82
84
|
this.config = config;
|
|
@@ -96,58 +98,23 @@ class CurlImpersonateFetcher {
|
|
|
96
98
|
const info = (0, curlBinaryResolver_1.checkCurlImpersonateAvailable)(target, this.config.libraryPath || null);
|
|
97
99
|
this.available = info.exists;
|
|
98
100
|
this.binaryPath = info.exists ? info.path : null;
|
|
101
|
+
this.useImpersonateFlag = info.useImpersonateFlag;
|
|
99
102
|
return this.available;
|
|
100
103
|
}
|
|
101
104
|
/**
|
|
102
|
-
*
|
|
103
|
-
|
|
104
|
-
isSystemCurlAvailable() {
|
|
105
|
-
if (this.systemCurlAvailable !== null) {
|
|
106
|
-
return this.systemCurlAvailable;
|
|
107
|
-
}
|
|
108
|
-
try {
|
|
109
|
-
(0, child_process_1.execSync)("curl --version", { stdio: "ignore" });
|
|
110
|
-
this.systemCurlAvailable = true;
|
|
111
|
-
return true;
|
|
112
|
-
}
|
|
113
|
-
catch {
|
|
114
|
-
this.systemCurlAvailable = false;
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
/**
|
|
119
|
-
* Perform HTTP GET request using curl-impersonate or system curl
|
|
105
|
+
* Perform HTTP GET request using curl-impersonate.
|
|
106
|
+
* This is the only supported request method – no fallback.
|
|
120
107
|
*/
|
|
121
108
|
async get(url, referer, cookieHeader, timeoutMs) {
|
|
122
|
-
//
|
|
123
|
-
if (this.isAvailable()) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
// If curl-impersonate fails, fall back to system curl
|
|
130
|
-
console.warn(`curl-impersonate returned status ${result.status}, falling back to system curl`);
|
|
131
|
-
}
|
|
132
|
-
catch (error) {
|
|
133
|
-
console.warn(`curl-impersonate error: ${error instanceof Error ? error.message : "Unknown error"}, falling back to system curl`);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
// Try system curl with enhanced headers
|
|
137
|
-
if (this.isSystemCurlAvailable()) {
|
|
138
|
-
try {
|
|
139
|
-
const result = await this.getSystemCurl(url, referer, cookieHeader, timeoutMs);
|
|
140
|
-
if (this.isSuccessStatus(result.status)) {
|
|
141
|
-
return result;
|
|
142
|
-
}
|
|
143
|
-
console.warn(`system curl returned status ${result.status}, falling back to enhanced fetch`);
|
|
144
|
-
}
|
|
145
|
-
catch (error) {
|
|
146
|
-
console.warn(`system curl error: ${error instanceof Error ? error.message : "Unknown error"}, falling back to enhanced fetch`);
|
|
147
|
-
}
|
|
109
|
+
// Ensure binary path is resolved (isAvailable caches the result).
|
|
110
|
+
if (!this.isAvailable()) {
|
|
111
|
+
return {
|
|
112
|
+
status: 0,
|
|
113
|
+
body: "",
|
|
114
|
+
error: "curl-impersonate binary not found",
|
|
115
|
+
};
|
|
148
116
|
}
|
|
149
|
-
|
|
150
|
-
return this.getEnhancedFetch(url, referer, cookieHeader, timeoutMs);
|
|
117
|
+
return this.getCurlImpersonate(url, referer, cookieHeader, timeoutMs);
|
|
151
118
|
}
|
|
152
119
|
getCookieJarFilePath(url) {
|
|
153
120
|
try {
|
|
@@ -181,19 +148,26 @@ class CurlImpersonateFetcher {
|
|
|
181
148
|
*/
|
|
182
149
|
async getCurlImpersonate(url, referer, cookieHeader, timeoutMs) {
|
|
183
150
|
const binaryPath = this.binaryPath;
|
|
184
|
-
const rawArgs = this.buildCurlArgs(url, referer, cookieHeader, timeoutMs
|
|
185
|
-
|
|
151
|
+
const rawArgs = this.buildCurlArgs(url, referer, cookieHeader, timeoutMs);
|
|
152
|
+
// When using the main `curl-impersonate` binary directly, prepend the
|
|
153
|
+
// --impersonate flag so it selects the correct TLS profile.
|
|
154
|
+
const target = this.config.target || "chrome116";
|
|
155
|
+
const impersonatePrefix = this.useImpersonateFlag
|
|
156
|
+
? ["--impersonate", target]
|
|
157
|
+
: [];
|
|
158
|
+
const argsWithTarget = [...impersonatePrefix, ...rawArgs];
|
|
159
|
+
const spawnPlanRaw = buildCurlSpawnCommand(binaryPath, argsWithTarget);
|
|
186
160
|
const cookieJarFile = this.getCookieJarFilePath(url);
|
|
187
161
|
const argsWithJar = (() => {
|
|
188
162
|
if (!cookieJarFile) {
|
|
189
|
-
return
|
|
163
|
+
return argsWithTarget;
|
|
190
164
|
}
|
|
191
165
|
const jarPath = spawnPlanRaw.mode === "wsl" ? toWslPath(cookieJarFile) : cookieJarFile;
|
|
192
166
|
if (!jarPath) {
|
|
193
|
-
return
|
|
167
|
+
return argsWithTarget;
|
|
194
168
|
}
|
|
195
169
|
// Read & write cookies (helps Cloudflare/JavDB session cookies persist between requests).
|
|
196
|
-
return [...
|
|
170
|
+
return [...argsWithTarget, "--cookie", jarPath, "--cookie-jar", jarPath];
|
|
197
171
|
})();
|
|
198
172
|
const spawnPlan = spawnPlanRaw.mode === "wsl"
|
|
199
173
|
? { command: "wsl", args: ["--exec", toWslPath(binaryPath), ...stripCacertArgs(argsWithJar)], mode: "wsl" }
|
|
@@ -277,125 +251,10 @@ class CurlImpersonateFetcher {
|
|
|
277
251
|
}
|
|
278
252
|
});
|
|
279
253
|
}
|
|
280
|
-
/**
|
|
281
|
-
* Perform HTTP GET request using system curl with enhanced headers
|
|
282
|
-
*/
|
|
283
|
-
async getSystemCurl(url, referer, cookieHeader, timeoutMs) {
|
|
284
|
-
const baseArgs = this.buildCurlArgs(url, referer, cookieHeader, timeoutMs, false);
|
|
285
|
-
const cookieJarFile = this.getCookieJarFilePath(url);
|
|
286
|
-
const args = cookieJarFile ? [...baseArgs, "--cookie", cookieJarFile, "--cookie-jar", cookieJarFile] : baseArgs;
|
|
287
|
-
return new Promise((resolve) => {
|
|
288
|
-
let child = null;
|
|
289
|
-
let stdout = "";
|
|
290
|
-
let stderr = "";
|
|
291
|
-
let resolved = false;
|
|
292
|
-
const timeout = Math.max(1000, timeoutMs);
|
|
293
|
-
const timeoutTimer = setTimeout(() => {
|
|
294
|
-
if (!resolved) {
|
|
295
|
-
resolved = true;
|
|
296
|
-
try {
|
|
297
|
-
child?.kill();
|
|
298
|
-
}
|
|
299
|
-
catch {
|
|
300
|
-
// ignore
|
|
301
|
-
}
|
|
302
|
-
resolve({
|
|
303
|
-
status: 0,
|
|
304
|
-
body: "",
|
|
305
|
-
error: `Request timeout after ${timeout}ms`,
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
}, timeout);
|
|
309
|
-
try {
|
|
310
|
-
child = (0, child_process_1.spawn)("curl", args, {
|
|
311
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
312
|
-
});
|
|
313
|
-
child.stdout?.on("data", (data) => {
|
|
314
|
-
stdout += data.toString("utf-8");
|
|
315
|
-
});
|
|
316
|
-
child.stderr?.on("data", (data) => {
|
|
317
|
-
stderr += data.toString("utf-8");
|
|
318
|
-
});
|
|
319
|
-
child.on("close", (code) => {
|
|
320
|
-
clearTimeout(timeoutTimer);
|
|
321
|
-
if (resolved) {
|
|
322
|
-
return;
|
|
323
|
-
}
|
|
324
|
-
resolved = true;
|
|
325
|
-
if (code === 0) {
|
|
326
|
-
const parsed = parseCurlWriteOutOutput(stdout);
|
|
327
|
-
const error = this.isSuccessStatus(parsed.status) ? undefined : `HTTP ${parsed.status}`;
|
|
328
|
-
resolve({ status: parsed.status, body: parsed.body, error });
|
|
329
|
-
}
|
|
330
|
-
else {
|
|
331
|
-
const errorMsg = stderr.trim() || `curl exited with code ${code}`;
|
|
332
|
-
resolve({
|
|
333
|
-
status: 0,
|
|
334
|
-
body: "",
|
|
335
|
-
error: errorMsg,
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
});
|
|
339
|
-
child.on("error", (err) => {
|
|
340
|
-
clearTimeout(timeoutTimer);
|
|
341
|
-
if (resolved) {
|
|
342
|
-
return;
|
|
343
|
-
}
|
|
344
|
-
resolved = true;
|
|
345
|
-
resolve({
|
|
346
|
-
status: 0,
|
|
347
|
-
body: "",
|
|
348
|
-
error: `Failed to spawn curl: ${err.message}`,
|
|
349
|
-
});
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
catch (err) {
|
|
353
|
-
clearTimeout(timeoutTimer);
|
|
354
|
-
if (resolved) {
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
resolved = true;
|
|
358
|
-
resolve({
|
|
359
|
-
status: 0,
|
|
360
|
-
body: "",
|
|
361
|
-
error: err instanceof Error ? err.message : "Unknown error",
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
/**
|
|
367
|
-
* Perform HTTP GET request using enhanced fetch with Cloudflare bypass headers
|
|
368
|
-
*/
|
|
369
|
-
async getEnhancedFetch(url, referer, cookieHeader, timeoutMs) {
|
|
370
|
-
const headers = this.buildEnhancedHeaders(referer, cookieHeader);
|
|
371
|
-
const controller = new AbortController();
|
|
372
|
-
const timeoutTimer = setTimeout(() => controller.abort(), timeoutMs);
|
|
373
|
-
try {
|
|
374
|
-
const response = await fetch(url, {
|
|
375
|
-
method: "GET",
|
|
376
|
-
headers,
|
|
377
|
-
signal: controller.signal,
|
|
378
|
-
});
|
|
379
|
-
clearTimeout(timeoutTimer);
|
|
380
|
-
const body = await response.text();
|
|
381
|
-
return {
|
|
382
|
-
status: response.status,
|
|
383
|
-
body,
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
catch (error) {
|
|
387
|
-
clearTimeout(timeoutTimer);
|
|
388
|
-
return {
|
|
389
|
-
status: 0,
|
|
390
|
-
body: "",
|
|
391
|
-
error: error instanceof Error ? error.message : "Unknown error",
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
254
|
/**
|
|
396
255
|
* Build curl command line arguments
|
|
397
256
|
*/
|
|
398
|
-
buildCurlArgs(url, referer, cookieHeader, timeoutMs
|
|
257
|
+
buildCurlArgs(url, referer, cookieHeader, timeoutMs) {
|
|
399
258
|
const args = [];
|
|
400
259
|
// Basic options
|
|
401
260
|
args.push("--silent");
|
|
@@ -423,72 +282,10 @@ class CurlImpersonateFetcher {
|
|
|
423
282
|
if (cookieHeader) {
|
|
424
283
|
args.push("--cookie", cookieHeader);
|
|
425
284
|
}
|
|
426
|
-
// Add enhanced headers for system curl
|
|
427
|
-
if (!useImpersonate) {
|
|
428
|
-
const chromeVersion = "144.0.0.0";
|
|
429
|
-
const chromeMajor = "144";
|
|
430
|
-
args.push("--user-agent", `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`);
|
|
431
|
-
args.push("--header", `Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7`);
|
|
432
|
-
args.push("--header", `Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,ja;q=0.6`);
|
|
433
|
-
args.push("--header", `Accept-Encoding: gzip, deflate, br`);
|
|
434
|
-
args.push("--header", `Connection: keep-alive`);
|
|
435
|
-
args.push("--header", `Upgrade-Insecure-Requests: 1`);
|
|
436
|
-
args.push("--header", `Sec-Fetch-Dest: document`);
|
|
437
|
-
args.push("--header", `Sec-Fetch-Mode: navigate`);
|
|
438
|
-
args.push("--header", `Sec-Fetch-Site: ${referer ? "same-origin" : "none"}`);
|
|
439
|
-
args.push("--header", `Sec-Fetch-User: ?1`);
|
|
440
|
-
args.push("--header", `Cache-Control: max-age=0`);
|
|
441
|
-
args.push("--header", `sec-ch-ua: "Google Chrome";v="${chromeMajor}", "Chromium";v="${chromeMajor}", "Not_A Brand";v="24"`);
|
|
442
|
-
args.push("--header", `sec-ch-ua-mobile: ?0`);
|
|
443
|
-
args.push("--header", `sec-ch-ua-platform: "Windows"`);
|
|
444
|
-
args.push("--header", `DNT: 1`);
|
|
445
|
-
}
|
|
446
285
|
// URL
|
|
447
286
|
args.push(url);
|
|
448
287
|
return args;
|
|
449
288
|
}
|
|
450
|
-
/**
|
|
451
|
-
* Build enhanced headers for Cloudflare bypass
|
|
452
|
-
*/
|
|
453
|
-
buildEnhancedHeaders(referer, cookieHeader) {
|
|
454
|
-
const chromeVersion = "144.0.0.0";
|
|
455
|
-
const chromeMajor = "144";
|
|
456
|
-
const headers = {
|
|
457
|
-
// Standard browser headers
|
|
458
|
-
"User-Agent": `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/${chromeVersion} Safari/537.36`,
|
|
459
|
-
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
|
460
|
-
"Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,ja;q=0.6",
|
|
461
|
-
"Accept-Encoding": "gzip, deflate, br",
|
|
462
|
-
"Connection": "keep-alive",
|
|
463
|
-
"Upgrade-Insecure-Requests": "1",
|
|
464
|
-
"Sec-Fetch-Dest": "document",
|
|
465
|
-
"Sec-Fetch-Mode": "navigate",
|
|
466
|
-
"Sec-Fetch-Site": referer ? "same-origin" : "none",
|
|
467
|
-
"Sec-Fetch-User": "?1",
|
|
468
|
-
"Cache-Control": "max-age=0",
|
|
469
|
-
// Client Hints (important for Cloudflare)
|
|
470
|
-
"sec-ch-ua": `"Google Chrome";v="${chromeMajor}", "Chromium";v="${chromeMajor}", "Not_A Brand";v="24"`,
|
|
471
|
-
"sec-ch-ua-mobile": "?0",
|
|
472
|
-
"sec-ch-ua-platform": `"Windows"`,
|
|
473
|
-
// Additional client hints that help bypass detection
|
|
474
|
-
"Sec-CH-UA-Arch": '"x86"',
|
|
475
|
-
"Sec-CH-UA-Bitness": '"64"',
|
|
476
|
-
"Sec-CH-UA-Full-Version": `"${chromeVersion}"`,
|
|
477
|
-
"Sec-CH-UA-Model": '""',
|
|
478
|
-
"Sec-CH-UA-Prefers-Color-Scheme": '"light"',
|
|
479
|
-
"Sec-CH-UA-Prefers-Reduced-Motion": '"no-reduced-motion"',
|
|
480
|
-
// Additional headers that may help
|
|
481
|
-
DNT: "1",
|
|
482
|
-
"Sec-Purpose": "prefetch",
|
|
483
|
-
};
|
|
484
|
-
if (referer) {
|
|
485
|
-
headers.Referer = referer;
|
|
486
|
-
}
|
|
487
|
-
if (cookieHeader) {
|
|
488
|
-
headers.Cookie = cookieHeader;
|
|
489
|
-
}
|
|
490
|
-
return headers;
|
|
491
|
-
}
|
|
492
289
|
/**
|
|
493
290
|
* Check if status is successful
|
|
494
291
|
*/
|
package/dist/data/javdb.js
CHANGED
|
@@ -44,12 +44,10 @@ const RetryBaseDelayMs = 1000;
|
|
|
44
44
|
const PreferredLocale = "zh";
|
|
45
45
|
class JavDbWebScraper {
|
|
46
46
|
config;
|
|
47
|
-
userAgents;
|
|
48
47
|
cookieJar = new Map();
|
|
49
48
|
curlFetcher;
|
|
50
49
|
constructor(config) {
|
|
51
50
|
this.config = config;
|
|
52
|
-
this.userAgents = buildUserAgentCandidates(config);
|
|
53
51
|
this.curlFetcher = new curlImpersonateFetcher_1.CurlImpersonateFetcher(config.curlImpersonate);
|
|
54
52
|
}
|
|
55
53
|
get serviceName() {
|
|
@@ -159,44 +157,10 @@ class JavDbWebScraper {
|
|
|
159
157
|
}
|
|
160
158
|
return { status: 0, body: "", error: lastError };
|
|
161
159
|
}
|
|
162
|
-
async sendRequest(url, referer,
|
|
160
|
+
async sendRequest(url, referer, _attemptIndex, timeoutMs) {
|
|
163
161
|
const cookieHeader = this.getCookieHeader(url);
|
|
164
162
|
const timeout = timeoutMs ?? this.config.requestTimeout;
|
|
165
|
-
|
|
166
|
-
if (this.curlFetcher.isAvailable()) {
|
|
167
|
-
try {
|
|
168
|
-
const result = await this.curlFetcher.get(url, referer, cookieHeader, timeout);
|
|
169
|
-
if (isSuccessStatus(result.status)) {
|
|
170
|
-
return { status: result.status, body: result.body };
|
|
171
|
-
}
|
|
172
|
-
// If curl-impersonate fails, fall back to standard fetch
|
|
173
|
-
console.warn(`curl-impersonate failed: ${result.error}, falling back to standard fetch`);
|
|
174
|
-
}
|
|
175
|
-
catch (error) {
|
|
176
|
-
console.warn(`curl-impersonate error: ${error instanceof Error ? error.message : "Unknown error"}, falling back to standard fetch`);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
// Fallback to standard fetch
|
|
180
|
-
const userAgent = this.userAgents[attemptIndex % this.userAgents.length] ?? defaultUserAgent;
|
|
181
|
-
const headers = buildChromeHeaders(userAgent, referer);
|
|
182
|
-
if (cookieHeader) {
|
|
183
|
-
headers.Cookie = cookieHeader;
|
|
184
|
-
}
|
|
185
|
-
const controller = new AbortController();
|
|
186
|
-
const timeoutTimer = setTimeout(() => controller.abort(), timeout);
|
|
187
|
-
try {
|
|
188
|
-
const response = await fetch(url, {
|
|
189
|
-
method: "GET",
|
|
190
|
-
headers,
|
|
191
|
-
signal: controller.signal,
|
|
192
|
-
});
|
|
193
|
-
const body = await response.text();
|
|
194
|
-
this.captureCookies(url, response.headers);
|
|
195
|
-
return { status: response.status, body };
|
|
196
|
-
}
|
|
197
|
-
finally {
|
|
198
|
-
clearTimeout(timeoutTimer);
|
|
199
|
-
}
|
|
163
|
+
return this.curlFetcher.get(url, referer, cookieHeader, timeout);
|
|
200
164
|
}
|
|
201
165
|
seedCookiesForUrl(baseUrl) {
|
|
202
166
|
if (!baseUrl) {
|
|
@@ -218,21 +182,6 @@ class JavDbWebScraper {
|
|
|
218
182
|
.map(([key, value]) => `${key}=${value}`)
|
|
219
183
|
.join("; ");
|
|
220
184
|
}
|
|
221
|
-
captureCookies(url, headers) {
|
|
222
|
-
const host = new URL(url).host;
|
|
223
|
-
const jar = this.cookieJar.get(host) ?? new Map();
|
|
224
|
-
const setCookies = typeof headers.getSetCookie === "function"
|
|
225
|
-
? headers.getSetCookie()
|
|
226
|
-
: (headers.get("set-cookie") ? [headers.get("set-cookie")] : []);
|
|
227
|
-
for (const raw of setCookies) {
|
|
228
|
-
const [pair] = raw.split(";", 1);
|
|
229
|
-
const [name, value] = pair.split("=", 2);
|
|
230
|
-
if (name && value) {
|
|
231
|
-
jar.set(name.trim(), value.trim());
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
this.cookieJar.set(host, jar);
|
|
235
|
-
}
|
|
236
185
|
}
|
|
237
186
|
exports.JavDbWebScraper = JavDbWebScraper;
|
|
238
187
|
function buildBaseUrls(config) {
|
|
@@ -241,56 +190,6 @@ function buildBaseUrls(config) {
|
|
|
241
190
|
.filter((url) => url.length > 0);
|
|
242
191
|
return Array.from(new Set(urls));
|
|
243
192
|
}
|
|
244
|
-
function buildUserAgentCandidates(config) {
|
|
245
|
-
const candidates = [];
|
|
246
|
-
if (config.userAgent?.trim()) {
|
|
247
|
-
candidates.push(config.userAgent.trim());
|
|
248
|
-
}
|
|
249
|
-
candidates.push(defaultUserAgent);
|
|
250
|
-
candidates.push("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36");
|
|
251
|
-
candidates.push("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36");
|
|
252
|
-
return Array.from(new Set(candidates));
|
|
253
|
-
}
|
|
254
|
-
function buildChromeHeaders(userAgent, referer) {
|
|
255
|
-
const chromeMajor = parseChromeMajorVersion(userAgent);
|
|
256
|
-
const platform = getPlatformFromUserAgent(userAgent);
|
|
257
|
-
const mobile = userAgent.toLowerCase().includes("mobile") ? "?1" : "?0";
|
|
258
|
-
const headers = {
|
|
259
|
-
Connection: "keep-alive",
|
|
260
|
-
"Cache-Control": "max-age=0",
|
|
261
|
-
"sec-ch-ua": `"Google Chrome";v="${chromeMajor}", "Chromium";v="${chromeMajor}", "Not_A Brand";v="24"`,
|
|
262
|
-
"sec-ch-ua-mobile": mobile,
|
|
263
|
-
"sec-ch-ua-platform": `"${platform}"`,
|
|
264
|
-
DNT: "1",
|
|
265
|
-
"Upgrade-Insecure-Requests": "1",
|
|
266
|
-
"User-Agent": userAgent,
|
|
267
|
-
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
|
|
268
|
-
"Sec-Fetch-Site": referer ? "same-origin" : "none",
|
|
269
|
-
"Sec-Fetch-Mode": "navigate",
|
|
270
|
-
"Sec-Fetch-User": "?1",
|
|
271
|
-
"Sec-Fetch-Dest": "document",
|
|
272
|
-
"Accept-Encoding": "gzip, deflate, br",
|
|
273
|
-
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7",
|
|
274
|
-
};
|
|
275
|
-
if (referer) {
|
|
276
|
-
headers.Referer = referer;
|
|
277
|
-
}
|
|
278
|
-
return headers;
|
|
279
|
-
}
|
|
280
|
-
function parseChromeMajorVersion(userAgent) {
|
|
281
|
-
const match = userAgent.match(/Chrome\/(\d+)/);
|
|
282
|
-
return match?.[1] ?? "131";
|
|
283
|
-
}
|
|
284
|
-
function getPlatformFromUserAgent(userAgent) {
|
|
285
|
-
const ua = userAgent.toLowerCase();
|
|
286
|
-
if (ua.includes("windows"))
|
|
287
|
-
return "Windows";
|
|
288
|
-
if (ua.includes("mac os x") || ua.includes("macintosh"))
|
|
289
|
-
return "macOS";
|
|
290
|
-
if (ua.includes("linux"))
|
|
291
|
-
return "Linux";
|
|
292
|
-
return "Windows";
|
|
293
|
-
}
|
|
294
193
|
function isSuccessStatus(status) {
|
|
295
194
|
return status >= 200 && status < 300;
|
|
296
195
|
}
|
|
@@ -643,4 +542,3 @@ function emptySearchResult(javId) {
|
|
|
643
542
|
cachedAt: undefined,
|
|
644
543
|
};
|
|
645
544
|
}
|
|
646
|
-
const defaultUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"items":{"MIAA-710":{"javId":"MIAA-710","title":"MIAA-710 性欲強過頭的我女友居然搞外遇!? 當作處罰一面禁止做愛一面吞精挑逗一周 禁欲解禁後、和好做愛持續中出中出。 白桃花 顯示原標題 【FANZA限定】性欲が強すぎる僕の彼女がまさかの浮気!? 罰としてSEX我慢させながら1週間ごっくんで焦らしまくって禁欲解禁後、仲直りSEXで何度も中出ししまくった。 白桃はな 生写真3枚付き","coverUrl":"https://c0.jdbstatic.com/covers/8v/8Vq5A9.jpg","releaseDate":"2022-10-19","duration":0,"director":"麒麟","maker":"MOODYZ","publisher":"","series":"","actors":["白桃はな","TECH"],"categories":["中出","單體作品","苗條","口交","吞精","美少女電影","無碼破解"],"torrents":[{"title":"MIAA-710","magnetLink":"magnet:?xt=urn:btih:76eae498f6ccf49c9749ec8ecf2f876e11c6be3a&dn=[javdb.com]MIAA-710","size":5647881994,"hasSubtitle":true,"hasUncensoredMarker":false,"uncensoredMarkerType":"None","hasHd":true,"seeders":0,"leechers":0,"sourceSite":"JavDB","dlSpeed":0,"eta":0,"weightScore":0},{"title":"MIAA-710-C.torrent","magnetLink":"magnet:?xt=urn:btih:dbce95a340dd108398a34f1abe3758c48dd64648&dn=[javdb.com]MIAA-710-C.torrent","size":5368709120,"hasSubtitle":true,"hasUncensoredMarker":false,"uncensoredMarkerType":"None","hasHd":true,"seeders":0,"leechers":0,"sourceSite":"JavDB","dlSpeed":0,"eta":0,"weightScore":0},{"title":"MIAA-710-U.torrent.无码破解","magnetLink":"magnet:?xt=urn:btih:62d6bbd5edf534d04e58fb7376ab6fb3d4c2cea2&dn=[javdb.com]MIAA-710-U.torrent.无码破解","size":6893422510,"hasSubtitle":false,"hasUncensoredMarker":true,"uncensoredMarkerType":"U","hasHd":true,"seeders":0,"leechers":0,"sourceSite":"JavDB","dlSpeed":0,"eta":0,"weightScore":0},{"title":"MIAA-710","magnetLink":"magnet:?xt=urn:btih:f389875882578a00143798dcbb09d5b22bd94932&dn=[javdb.com]MIAA-710","size":5433133629,"hasSubtitle":false,"hasUncensoredMarker":false,"uncensoredMarkerType":"None","hasHd":true,"seeders":0,"leechers":0,"sourceSite":"JavDB","dlSpeed":0,"eta":0,"weightScore":0},{"title":"miaa-710.torrent","magnetLink":"magnet:?xt=urn:btih:0633d1e82351e9f7cdeff212755746e2eff0597c&dn=[javdb.com]miaa-710.torrent","size":5336496865,"hasSubtitle":false,"hasUncensoredMarker":false,"uncensoredMarkerType":"None","hasHd":true,"seeders":0,"leechers":0,"sourceSite":"JavDB","dlSpeed":0,"eta":0,"weightScore":0},{"title":"MIAA-710","magnetLink":"magnet:?xt=urn:btih:54b0c7957079ade34996810b678a2c1559d9e8a0&dn=[javdb.com]MIAA-710","size":1825361100,"hasSubtitle":false,"hasUncensoredMarker":false,"uncensoredMarkerType":"None","hasHd":false,"seeders":0,"leechers":0,"sourceSite":"JavDB","dlSpeed":0,"eta":0,"weightScore":0},{"title":"MIAA-710","magnetLink":"magnet:?xt=urn:btih:56cd13a9f152838dd6775ffd339f6b97a355c79c&dn=[javdb.com]MIAA-710","size":5433133629,"hasSubtitle":false,"hasUncensoredMarker":false,"uncensoredMarkerType":"None","hasHd":true,"seeders":0,"leechers":0,"sourceSite":"JavDB","dlSpeed":0,"eta":0,"weightScore":0},{"title":"MIAA-710","magnetLink":"magnet:?xt=urn:btih:7c7c7d904f7ec846b6a55619d04c83580b6267e5&dn=[javdb.com]MIAA-710","size":5486820720,"hasSubtitle":false,"hasUncensoredMarker":false,"uncensoredMarkerType":"None","hasHd":true,"seeders":0,"leechers":0,"sourceSite":"JavDB","dlSpeed":0,"eta":0,"weightScore":0},{"title":"MIAA-710","magnetLink":"magnet:?xt=urn:btih:0ee52547c8f4fb6acf892cc938f25cfb30d8afb4&dn=[javdb.com]MIAA-710","size":5443871047,"hasSubtitle":false,"hasUncensoredMarker":false,"uncensoredMarkerType":"None","hasHd":true,"seeders":0,"leechers":0,"sourceSite":"JavDB","dlSpeed":0,"eta":0,"weightScore":0},{"title":"@miaa710","magnetLink":"magnet:?xt=urn:btih:5dfd38d63f18993b440ad9ba4fe7e31a81a39f0c&dn=[javdb.com]@miaa710","size":4048006676,"hasSubtitle":false,"hasUncensoredMarker":false,"uncensoredMarkerType":"None","hasHd":true,"seeders":0,"leechers":0,"sourceSite":"JavDB","dlSpeed":0,"eta":0,"weightScore":0},{"title":"MIAA-710-uncensored-HD","magnetLink":"magnet:?xt=urn:btih:8483028491f91c4fb4a5a45c1bebd4f4f7f804cb&dn=[javdb.com]MIAA-710-uncensored-HD","size":3543348019,"hasSubtitle":false,"hasUncensoredMarker":true,"uncensoredMarkerType":"U","hasHd":true,"seeders":0,"leechers":0,"sourceSite":"JavDB","dlSpeed":0,"eta":0,"weightScore":0},{"title":"HD_MIAA-710","magnetLink":"magnet:?xt=urn:btih:3f6708da3efaa19ddb29c812e5e91b5df6cd91a2&dn=[javdb.com]HD_MIAA-710","size":2426656522,"hasSubtitle":false,"hasUncensoredMarker":false,"uncensoredMarkerType":"None","hasHd":true,"seeders":0,"leechers":0,"sourceSite":"JavDB","dlSpeed":0,"eta":0,"weightScore":0},{"title":"MIAA-710","magnetLink":"magnet:?xt=urn:btih:f3dd616df127bd25b4a19c236a33f0fbbe11697e&dn=[javdb.com]MIAA-710","size":2319282339,"hasSubtitle":false,"hasUncensoredMarker":false,"uncensoredMarkerType":"None","hasHd":false,"seeders":0,"leechers":0,"sourceSite":"JavDB","dlSpeed":0,"eta":0,"weightScore":0},{"title":"[Mosaic Removed Uncensored ]MIAA-710 性欲が強すぎる僕の彼女がまさかの浮気!? 罰としてSEX我慢させながら1週間ごっくんで焦らしまくって禁欲解禁後、仲直りSEXで何度も中出ししまくった。 白桃はな.TS","magnetLink":"magnet:?xt=urn:btih:349f34484c14dd5a015ba89c19da88acd753a995&dn=[javdb.com][Mosaic Removed Uncensored ]MIAA-710 性欲が強すぎる僕の彼女がまさかの浮気!? 罰としてSEX我慢させながら1週間ごっくんで焦らしまくって禁欲解禁後、仲直りSEXで何度も中出ししまくった。 白桃はな.TS","size":2244120412,"hasSubtitle":false,"hasUncensoredMarker":true,"uncensoredMarkerType":"U","hasHd":false,"seeders":0,"leechers":0,"sourceSite":"JavDB","dlSpeed":0,"eta":0,"weightScore":0},{"title":"MIAA-710","magnetLink":"magnet:?xt=urn:btih:b05c2c1d65190308841b68048ece59296d85a727&dn=[javdb.com]MIAA-710","size":1879048192,"hasSubtitle":false,"hasUncensoredMarker":false,"uncensoredMarkerType":"None","hasHd":false,"seeders":0,"leechers":0,"sourceSite":"JavDB","dlSpeed":0,"eta":0,"weightScore":0},{"title":"MIAA-710","magnetLink":"magnet:?xt=urn:btih:10a473daf0c829f337c3287209408ba9090c1ed4&dn=[javdb.com]MIAA-710","size":1320702443,"hasSubtitle":false,"hasUncensoredMarker":false,"uncensoredMarkerType":"None","hasHd":false,"seeders":0,"leechers":0,"sourceSite":"JavDB","dlSpeed":0,"eta":0,"weightScore":0},{"title":"miaa-710","magnetLink":"magnet:?xt=urn:btih:6382195c130804eacc743b4531ba2e1b3090e423&dn=[javdb.com]miaa-710","size":5529770393,"hasSubtitle":false,"hasUncensoredMarker":false,"uncensoredMarkerType":"None","hasHd":true,"seeders":0,"leechers":0,"sourceSite":"JavDB","dlSpeed":0,"eta":0,"weightScore":0}],"detailUrl":"https://javdb.com/v/8Vq5A9?locale=zh","dataSource":"Local","cachedAt":"2026-02-08T08:34:51.358Z"}}}
|
|
@@ -4,12 +4,27 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getCurlImpersonateBinaryPath = getCurlImpersonateBinaryPath;
|
|
7
|
+
exports.getCurlImpersonateMainBinaryPath = getCurlImpersonateMainBinaryPath;
|
|
7
8
|
exports.getCurlCaBundlePath = getCurlCaBundlePath;
|
|
8
9
|
exports.checkCurlImpersonateAvailable = checkCurlImpersonateAvailable;
|
|
9
10
|
exports.getAvailableTargets = getAvailableTargets;
|
|
10
11
|
const path_1 = __importDefault(require("path"));
|
|
11
12
|
const fs_1 = __importDefault(require("fs"));
|
|
12
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Package root directory (works for both src/ and dist/ layouts).
|
|
15
|
+
* __dirname is either src/utils/ or dist/utils/ – two levels up is the package root.
|
|
16
|
+
*/
|
|
17
|
+
const packageRoot = path_1.default.resolve(__dirname, "..", "..");
|
|
18
|
+
function resolveRelativeToPackageRoot(filePath) {
|
|
19
|
+
if (!filePath) {
|
|
20
|
+
return filePath;
|
|
21
|
+
}
|
|
22
|
+
if (path_1.default.isAbsolute(filePath)) {
|
|
23
|
+
return filePath;
|
|
24
|
+
}
|
|
25
|
+
return path_1.default.resolve(packageRoot, filePath);
|
|
26
|
+
}
|
|
27
|
+
function resolveRelativeToCwd(filePath) {
|
|
13
28
|
if (!filePath) {
|
|
14
29
|
return filePath;
|
|
15
30
|
}
|
|
@@ -43,15 +58,18 @@ function findOnPath(fileName) {
|
|
|
43
58
|
return null;
|
|
44
59
|
}
|
|
45
60
|
/**
|
|
46
|
-
* Get the platform-specific curl-impersonate
|
|
61
|
+
* Get the platform-specific curl-impersonate target wrapper script path.
|
|
62
|
+
* Resolves vendored paths relative to the **package root** (not CWD) so that
|
|
63
|
+
* the binary is found regardless of where the user invokes the command.
|
|
47
64
|
*/
|
|
48
65
|
function getCurlImpersonateBinaryPath(target = "chrome116", configuredPath = null) {
|
|
66
|
+
// User-configured path is resolved relative to CWD (explicit override).
|
|
49
67
|
if (configuredPath && configuredPath.trim()) {
|
|
50
|
-
return
|
|
68
|
+
return resolveRelativeToCwd(configuredPath.trim());
|
|
51
69
|
}
|
|
52
70
|
const platform = process.platform;
|
|
53
71
|
const arch = process.arch;
|
|
54
|
-
// Map platform/arch to
|
|
72
|
+
// Map platform/arch to target wrapper script in third_party/curl-impersonate/
|
|
55
73
|
const binaryMap = {
|
|
56
74
|
win32: {
|
|
57
75
|
x64: path_1.default.join("third_party", "curl-impersonate", "bin", `curl_${target}.exe`),
|
|
@@ -71,7 +89,27 @@ function getCurlImpersonateBinaryPath(target = "chrome116", configuredPath = nul
|
|
|
71
89
|
return null;
|
|
72
90
|
}
|
|
73
91
|
const binaryPath = platformBinaries[arch] || platformBinaries.x64;
|
|
74
|
-
return
|
|
92
|
+
return resolveRelativeToPackageRoot(binaryPath);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get the path to the main `curl-impersonate` executable (not a target wrapper).
|
|
96
|
+
* Checks vendored location first, then falls back to PATH lookup.
|
|
97
|
+
*/
|
|
98
|
+
function getCurlImpersonateMainBinaryPath() {
|
|
99
|
+
const ext = process.platform === "win32" ? ".exe" : "";
|
|
100
|
+
const vendoredRelative = path_1.default.join("third_party", "curl-impersonate", "bin", `curl-impersonate${ext}`);
|
|
101
|
+
const vendored = resolveRelativeToPackageRoot(vendoredRelative);
|
|
102
|
+
if (isExecutableFile(vendored)) {
|
|
103
|
+
return vendored;
|
|
104
|
+
}
|
|
105
|
+
// Windows fallback: vendored Linux binary (to be run via WSL).
|
|
106
|
+
if (process.platform === "win32") {
|
|
107
|
+
const alt = resolveRelativeToPackageRoot(path_1.default.join("third_party", "curl-impersonate", "bin", "curl-impersonate"));
|
|
108
|
+
if (isExecutableFile(alt)) {
|
|
109
|
+
return alt;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return findOnPath(`curl-impersonate${ext}`);
|
|
75
113
|
}
|
|
76
114
|
/**
|
|
77
115
|
* Get the CA bundle path for curl-impersonate
|
|
@@ -101,41 +139,81 @@ function getCurlCaBundlePath(configuredPath = null) {
|
|
|
101
139
|
if (!platformCaBundles) {
|
|
102
140
|
return null;
|
|
103
141
|
}
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
142
|
+
const relative = platformCaBundles[arch] || platformCaBundles.x64;
|
|
143
|
+
const resolved = resolveRelativeToPackageRoot(relative);
|
|
144
|
+
if (fs_1.default.existsSync(resolved)) {
|
|
145
|
+
return resolved;
|
|
107
146
|
}
|
|
108
147
|
return null;
|
|
109
148
|
}
|
|
110
149
|
/**
|
|
111
|
-
* Check if curl-impersonate binary is available
|
|
150
|
+
* Check if curl-impersonate binary is available.
|
|
151
|
+
*
|
|
152
|
+
* On Linux/macOS the main `curl-impersonate` ELF binary is preferred because
|
|
153
|
+
* it is a standalone executable that does not depend on bash; the `curl_<target>`
|
|
154
|
+
* wrapper scripts are shell scripts requiring `#!/usr/bin/env bash` which may
|
|
155
|
+
* not be available in all environments.
|
|
156
|
+
*
|
|
157
|
+
* Resolution order (Linux/macOS):
|
|
158
|
+
* 1. User-configured path
|
|
159
|
+
* 2. Vendored main `curl-impersonate` binary (package root) – with `--impersonate` flag
|
|
160
|
+
* 3. `curl-impersonate` on PATH – with `--impersonate` flag
|
|
161
|
+
* 4. Vendored wrapper script `curl_<target>` (package root)
|
|
162
|
+
* 5. `curl_<target>` on PATH
|
|
163
|
+
*
|
|
164
|
+
* Resolution order (Windows):
|
|
165
|
+
* 1. User-configured path
|
|
166
|
+
* 2. Vendored wrapper `curl_<target>.exe` / non-`.exe` WSL fallback (package root)
|
|
167
|
+
* 3. `curl_<target>.exe` on PATH
|
|
168
|
+
* 4. Main `curl-impersonate` binary (vendored or PATH) – with `--impersonate` flag
|
|
112
169
|
*/
|
|
113
170
|
function checkCurlImpersonateAvailable(target = "chrome116", configuredPath = null) {
|
|
114
|
-
|
|
115
|
-
if (
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return { path: alt, exists: true, target };
|
|
171
|
+
// 1. User-configured path (resolved relative to CWD).
|
|
172
|
+
if (configuredPath && configuredPath.trim()) {
|
|
173
|
+
const binaryPath = getCurlImpersonateBinaryPath(target, configuredPath);
|
|
174
|
+
if (binaryPath && fs_1.default.existsSync(binaryPath)) {
|
|
175
|
+
return { path: binaryPath, exists: true, target, useImpersonateFlag: false };
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// On non-Windows, prefer the main binary (standalone ELF, no bash dependency).
|
|
179
|
+
if (process.platform !== "win32") {
|
|
180
|
+
const mainBinary = getCurlImpersonateMainBinaryPath();
|
|
181
|
+
if (mainBinary) {
|
|
182
|
+
return { path: mainBinary, exists: true, target, useImpersonateFlag: true };
|
|
127
183
|
}
|
|
128
184
|
}
|
|
129
|
-
//
|
|
185
|
+
// Vendored wrapper script lookup.
|
|
186
|
+
const binaryPath = getCurlImpersonateBinaryPath(target, null);
|
|
187
|
+
if (binaryPath) {
|
|
188
|
+
if (fs_1.default.existsSync(binaryPath)) {
|
|
189
|
+
return { path: binaryPath, exists: true, target, useImpersonateFlag: false };
|
|
190
|
+
}
|
|
191
|
+
// Windows fallback: allow vendored Linux binaries (to be run via WSL) when the .exe doesn't exist.
|
|
192
|
+
if (process.platform === "win32" && binaryPath.toLowerCase().endsWith(".exe")) {
|
|
193
|
+
const alt = binaryPath.slice(0, -4);
|
|
194
|
+
if (alt && fs_1.default.existsSync(alt)) {
|
|
195
|
+
return { path: alt, exists: true, target, useImpersonateFlag: false };
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// Allow PATH-installed curl_<target> binaries.
|
|
130
200
|
const fileName = process.platform === "win32" ? `curl_${target}.exe` : `curl_${target}`;
|
|
131
201
|
const found = findOnPath(fileName);
|
|
132
202
|
if (found) {
|
|
133
|
-
return { path: found, exists: true, target };
|
|
203
|
+
return { path: found, exists: true, target, useImpersonateFlag: false };
|
|
204
|
+
}
|
|
205
|
+
// Windows: main binary fallback (vendored or PATH) as last resort.
|
|
206
|
+
if (process.platform === "win32") {
|
|
207
|
+
const mainBinary = getCurlImpersonateMainBinaryPath();
|
|
208
|
+
if (mainBinary) {
|
|
209
|
+
return { path: mainBinary, exists: true, target, useImpersonateFlag: true };
|
|
210
|
+
}
|
|
134
211
|
}
|
|
135
212
|
return {
|
|
136
|
-
path: binaryPath,
|
|
213
|
+
path: binaryPath ?? "",
|
|
137
214
|
exists: false,
|
|
138
215
|
target,
|
|
216
|
+
useImpersonateFlag: false,
|
|
139
217
|
};
|
|
140
218
|
}
|
|
141
219
|
/**
|