mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-06-11 09:17:54 +02:00
checkpoint
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import path from "node:path";
|
||||
import { launchOptions } from "camoufox-js";
|
||||
import type { LaunchOptions } from "camoufox-js/dist/utils.js";
|
||||
import { launchOptions } from "donutbrowser-camoufox-js";
|
||||
import type { LaunchOptions } from "donutbrowser-camoufox-js/dist/utils.js";
|
||||
import {
|
||||
type CamoufoxConfig,
|
||||
deleteCamoufoxConfig,
|
||||
@@ -196,7 +196,7 @@ function convertCamoufoxToFingerprintGenerator(
|
||||
fingerprintObj.fonts = camoufoxFingerprint.fonts;
|
||||
}
|
||||
|
||||
return fingerprintObj;
|
||||
return { ...camoufoxFingerprint, ...fingerprintObj };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -465,7 +465,7 @@ export async function generateCamoufoxConfig(
|
||||
options: GenerateConfigOptions,
|
||||
): Promise<string> {
|
||||
try {
|
||||
const launchOpts: LaunchOptions = {
|
||||
const launchOpts: any = {
|
||||
headless: false,
|
||||
i_know_what_im_doing: true,
|
||||
config: {
|
||||
@@ -474,8 +474,8 @@ export async function generateCamoufoxConfig(
|
||||
},
|
||||
};
|
||||
|
||||
if (options.geoip !== undefined) {
|
||||
launchOpts.geoip = options.geoip;
|
||||
if (options.geoip) {
|
||||
launchOpts.geoip = true;
|
||||
}
|
||||
|
||||
if (options.blockImages) {
|
||||
@@ -492,11 +492,19 @@ export async function generateCamoufoxConfig(
|
||||
launchOpts.executable_path = options.executablePath;
|
||||
}
|
||||
|
||||
if (options.proxy) {
|
||||
launchOpts.proxy = options.proxy;
|
||||
}
|
||||
|
||||
// If fingerprint is provided, use it and ignore other options except executable_path and block_*
|
||||
if (options.fingerprint) {
|
||||
try {
|
||||
const camoufoxFingerprint = JSON.parse(options.fingerprint);
|
||||
|
||||
if (camoufoxFingerprint.timezone) {
|
||||
launchOpts.config.timezone = camoufoxFingerprint.timezone;
|
||||
}
|
||||
|
||||
// Convert camoufox fingerprint format to fingerprint-generator format
|
||||
const fingerprintObj =
|
||||
convertCamoufoxToFingerprintGenerator(camoufoxFingerprint);
|
||||
@@ -506,9 +514,6 @@ export async function generateCamoufoxConfig(
|
||||
}
|
||||
} else {
|
||||
// Use individual options to build configuration
|
||||
if (options.proxy) {
|
||||
launchOpts.proxy = options.proxy;
|
||||
}
|
||||
|
||||
if (options.maxWidth && options.maxHeight) {
|
||||
launchOpts.screen = {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import type { LaunchOptions } from "camoufox-js/dist/utils.js";
|
||||
import type { LaunchOptions } from "donutbrowser-camoufox-js/dist/utils.js";
|
||||
import tmp from "tmp";
|
||||
|
||||
export interface CamoufoxConfig {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { launchOptions } from "camoufox-js";
|
||||
import type { LaunchOptions } from "camoufox-js/dist/utils.js";
|
||||
import { launchOptions } from "donutbrowser-camoufox-js";
|
||||
import type { LaunchOptions } from "donutbrowser-camoufox-js/dist/utils.js";
|
||||
import { type Browser, type BrowserServer, firefox } from "playwright-core";
|
||||
import { getCamoufoxConfig, saveCamoufoxConfig } from "./camoufox-storage.js";
|
||||
import { getEnvVars } from "./utils.js";
|
||||
import { getEnvVars, parseProxyString } from "./utils.js";
|
||||
|
||||
/**
|
||||
* Run a Camoufox browser server as a worker process
|
||||
@@ -128,15 +128,31 @@ export async function runCamoufoxWorker(id: string): Promise<void> {
|
||||
"Failed to parse custom config, using generated config:",
|
||||
error,
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Launch the server with the final configuration
|
||||
server = await firefox.launchServer({
|
||||
const finalOptions: any = {
|
||||
...generatedOptions,
|
||||
wsPath: `/ws_${config.id}`,
|
||||
env: finalEnv,
|
||||
});
|
||||
};
|
||||
|
||||
// Only add proxy if it exists and is valid
|
||||
if (camoufoxOptions.proxy) {
|
||||
try {
|
||||
finalOptions.proxy = parseProxyString(camoufoxOptions.proxy);
|
||||
} catch (error) {
|
||||
console.error({
|
||||
message: "Failed to parse proxy, launching without proxy",
|
||||
error,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
server = await firefox.launchServer(finalOptions);
|
||||
|
||||
// Connect to the server
|
||||
browser = await firefox.connect(server.wsEndpoint());
|
||||
|
||||
+16
-29
@@ -1,5 +1,5 @@
|
||||
import type { LaunchOptions } from "camoufox-js/dist/utils.js";
|
||||
import { program } from "commander";
|
||||
import type { LaunchOptions } from "donutbrowser-camoufox-js/dist/utils.js";
|
||||
import {
|
||||
generateCamoufoxConfig,
|
||||
startCamoufoxProcess,
|
||||
@@ -165,7 +165,7 @@ program
|
||||
.option("--proxy <proxy>", "proxy URL for config generation")
|
||||
.option("--max-width <width>", "maximum screen width", parseInt)
|
||||
.option("--max-height <height>", "maximum screen height", parseInt)
|
||||
.option("--geoip [ip]", "enable geoip or specify IP")
|
||||
.option("--geoip", "enable geoip")
|
||||
.option("--block-images", "block images")
|
||||
.option("--block-webrtc", "block WebRTC")
|
||||
.option("--block-webgl", "block WebGL")
|
||||
@@ -325,7 +325,6 @@ program
|
||||
}),
|
||||
);
|
||||
process.exit(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,22 +373,6 @@ program
|
||||
process.exit(0);
|
||||
} else if (action === "generate-config") {
|
||||
try {
|
||||
// Handle geoip option properly
|
||||
let geoipValue: string | boolean = true; // Default to true
|
||||
if (options.geoip !== undefined) {
|
||||
if (typeof options.geoip === "boolean") {
|
||||
geoipValue = options.geoip;
|
||||
} else if (typeof options.geoip === "string") {
|
||||
if (options.geoip === "true") {
|
||||
geoipValue = true;
|
||||
} else if (options.geoip === "false") {
|
||||
geoipValue = false;
|
||||
} else {
|
||||
geoipValue = options.geoip; // IP address
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const config = await generateCamoufoxConfig({
|
||||
proxy:
|
||||
typeof options.proxy === "string" ? options.proxy : undefined,
|
||||
@@ -401,7 +384,7 @@ program
|
||||
typeof options.maxHeight === "number"
|
||||
? options.maxHeight
|
||||
: undefined,
|
||||
geoip: geoipValue,
|
||||
geoip: Boolean(options.geoip),
|
||||
blockImages:
|
||||
typeof options.blockImages === "boolean"
|
||||
? options.blockImages
|
||||
@@ -426,17 +409,18 @@ program
|
||||
console.log(config);
|
||||
process.exit(0);
|
||||
} catch (error: unknown) {
|
||||
console.error(
|
||||
`Failed to generate config: ${
|
||||
error instanceof Error ? error.message : JSON.stringify(error)
|
||||
}`,
|
||||
);
|
||||
console.error({
|
||||
error: "Failed to generate config",
|
||||
message:
|
||||
error instanceof Error ? error.message : JSON.stringify(error),
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
console.error(
|
||||
"Invalid action. Use 'start', 'stop', 'list', or 'generate-config'",
|
||||
);
|
||||
console.error({
|
||||
error: "Invalid action",
|
||||
message: "Use 'start', 'stop', 'list', or 'generate-config'",
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
},
|
||||
@@ -452,7 +436,10 @@ program
|
||||
if (action === "start") {
|
||||
await runCamoufoxWorker(options.id);
|
||||
} else {
|
||||
console.error("Invalid action for camoufox-worker. Use 'start'");
|
||||
console.error({
|
||||
error: "Invalid action for camoufox-worker",
|
||||
message: "Use 'start'",
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { LaunchOptions } from "playwright-core";
|
||||
|
||||
const OS_MAP: { [key: string]: "mac" | "win" | "lin" } = {
|
||||
darwin: "mac",
|
||||
linux: "lin",
|
||||
@@ -35,3 +37,81 @@ export function getEnvVars(configMap: Record<string, string>) {
|
||||
|
||||
return envVars;
|
||||
}
|
||||
|
||||
export function parseProxyString(proxyString: LaunchOptions["proxy"] | string) {
|
||||
if (typeof proxyString === "object") {
|
||||
return proxyString;
|
||||
}
|
||||
|
||||
if (!proxyString || typeof proxyString !== "string") {
|
||||
throw new Error("Invalid proxy string provided");
|
||||
}
|
||||
|
||||
// Remove any leading/trailing whitespace
|
||||
const trimmed = proxyString.trim();
|
||||
|
||||
// Handle different proxy string formats:
|
||||
// 1. http://username:password@host:port
|
||||
// 2. host:port
|
||||
// 3. protocol://host:port
|
||||
// 4. username:password@host:port
|
||||
|
||||
let server = "";
|
||||
let username: string | undefined;
|
||||
let password: string | undefined;
|
||||
|
||||
try {
|
||||
// Try parsing as URL first (handles protocol://username:password@host:port)
|
||||
if (trimmed.includes("://")) {
|
||||
const url = new URL(trimmed);
|
||||
server = `${url.hostname}:${url.port}`;
|
||||
|
||||
if (url.username) {
|
||||
username = decodeURIComponent(url.username);
|
||||
}
|
||||
if (url.password) {
|
||||
password = decodeURIComponent(url.password);
|
||||
}
|
||||
} else {
|
||||
// Handle formats without protocol
|
||||
let workingString = trimmed;
|
||||
|
||||
// Check for username:password@ prefix
|
||||
const authMatch = workingString.match(/^([^:@]+):([^@]+)@(.+)$/);
|
||||
if (authMatch) {
|
||||
username = authMatch[1];
|
||||
password = authMatch[2];
|
||||
workingString = authMatch[3];
|
||||
}
|
||||
|
||||
// The remaining part should be host:port
|
||||
server = workingString;
|
||||
}
|
||||
|
||||
// Validate that we have a server
|
||||
if (!server) {
|
||||
throw new Error("Could not extract server information");
|
||||
}
|
||||
|
||||
// Basic validation for host:port format
|
||||
if (!server.includes(":") || server.split(":").length !== 2) {
|
||||
throw new Error("Server must be in host:port format");
|
||||
}
|
||||
|
||||
const result: LaunchOptions["proxy"] = { server };
|
||||
|
||||
if (username !== undefined) {
|
||||
result.username = username;
|
||||
}
|
||||
|
||||
if (password !== undefined) {
|
||||
result.password = password;
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to parse proxy string: ${error instanceof Error ? error.message : "Unknown error"}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user