269 lines
9.1 KiB
TypeScript
269 lines
9.1 KiB
TypeScript
/**
|
|
* 浏览器路径工具
|
|
* 统一管理所有模块的浏览器可执行文件路径
|
|
* 支持自定义安装路径和离线使用
|
|
*/
|
|
|
|
import { app } from 'electron';
|
|
import path from 'path';
|
|
import fs from 'fs';
|
|
import { AppEnv } from '../mapi/env';
|
|
import { getRuntimeBundlePath } from './resource-path';
|
|
|
|
/**
|
|
* 获取Playwright Chromium浏览器可执行文件路径
|
|
* 优先使用打包的浏览器,支持自定义安装路径
|
|
*/
|
|
function getPlatformChromePaths(): string[] {
|
|
if (process.platform === 'win32') {
|
|
return ['chrome-win64/chrome.exe', 'chrome-win/chrome.exe'];
|
|
} else if (process.platform === 'darwin') {
|
|
return [
|
|
'chrome-mac/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing',
|
|
'chrome-mac-arm64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing',
|
|
'chrome-mac/Chromium.app/Contents/MacOS/Chromium',
|
|
'chrome-mac/chrome',
|
|
];
|
|
} else {
|
|
return ['chrome-linux/chrome'];
|
|
}
|
|
}
|
|
|
|
function getPlatformCacheBase(): string[] {
|
|
const runtimeCandidates = [
|
|
getRuntimeBundlePath('playwright'),
|
|
AppEnv.resourceBundleRoot ? path.join(AppEnv.resourceBundleRoot, 'playwright') : '',
|
|
AppEnv.dataRoot ? path.join(AppEnv.dataRoot, 'playwright') : '',
|
|
].filter(Boolean);
|
|
|
|
if (process.platform === 'win32') {
|
|
return [
|
|
...runtimeCandidates,
|
|
path.join(process.env.LOCALAPPDATA || '', 'ms-playwright'),
|
|
path.join(process.env.APPDATA || '', 'ms-playwright'),
|
|
];
|
|
} else if (process.platform === 'darwin') {
|
|
return [
|
|
...runtimeCandidates,
|
|
path.join(process.env.HOME || '', 'Library/Caches/ms-playwright'),
|
|
];
|
|
} else {
|
|
return [
|
|
...runtimeCandidates,
|
|
path.join(process.env.HOME || '', '.cache/ms-playwright'),
|
|
];
|
|
}
|
|
}
|
|
|
|
export function getPlaywrightChromiumPath(): string | undefined {
|
|
const runtimePath = getRuntimeBundlePath('playwright');
|
|
const basePaths = [
|
|
runtimePath,
|
|
path.join(process.resourcesPath || app.getAppPath(), 'extra/common/playwright'),
|
|
path.join(app.getAppPath(), 'resources/extra/common/playwright'),
|
|
path.join(app.getAppPath(), 'electron/resources/extra/common/playwright'),
|
|
].filter(Boolean);
|
|
|
|
const chromiumVersions = ['chromium-1200'];
|
|
const chromePaths = getPlatformChromePaths();
|
|
|
|
for (const basePath of basePaths) {
|
|
for (const version of chromiumVersions) {
|
|
for (const chromePath of chromePaths) {
|
|
const browserPath = path.join(basePath, version, chromePath);
|
|
if (fs.existsSync(browserPath)) {
|
|
console.log('✅ [Browser] 使用打包的浏览器:', browserPath);
|
|
return browserPath;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const playwrightCacheBase = getPlatformCacheBase();
|
|
|
|
// 也尝试查找任何可用的 chromium 版本(仅用于开发环境回退)
|
|
for (const cacheBase of playwrightCacheBase) {
|
|
if (fs.existsSync(cacheBase)) {
|
|
const allDirs = fs.readdirSync(cacheBase);
|
|
const chromiumDirs = allDirs.filter(d => d.startsWith('chromium-')).sort().reverse();
|
|
for (const version of chromiumDirs) {
|
|
for (const chromePath of chromePaths) {
|
|
const browserPath = path.join(cacheBase, version, chromePath);
|
|
if (fs.existsSync(browserPath)) {
|
|
console.log('⚠️ [Browser] 使用系统缓存的浏览器:', browserPath);
|
|
return browserPath;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log('⚠️ [Browser] 警告:未找到Playwright浏览器');
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* 获取用户数据目录(用于持久化浏览器数据)
|
|
*/
|
|
export function getBrowserUserDataDir(contextName: string): string {
|
|
return path.join(app.getPath('userData'), 'browser-data', contextName);
|
|
}
|
|
|
|
/**
|
|
* 通用的浏览器启动参数
|
|
* 参考 aigc-human 项目的反爬和性能优化配置
|
|
*/
|
|
export const COMMON_BROWSER_ARGS = [
|
|
// === 核心反爬与沙箱配置 ===
|
|
'--disable-blink-features=AutomationControlled',
|
|
'--no-sandbox',
|
|
'--disable-setuid-sandbox',
|
|
'--disable-dev-shm-usage',
|
|
'--disable-infobars',
|
|
|
|
// === 启动速度优化 ===
|
|
'--no-first-run',
|
|
'--no-default-browser-check',
|
|
'--disable-breakpad', // 禁用崩溃报告
|
|
'--disable-component-update', // 禁用组件自动更新
|
|
'--disable-sync', // 禁用同步
|
|
'--disable-default-apps',
|
|
'--disable-popup-blocking',
|
|
|
|
// === 后台性能优化 (防止页面被浏览器通过节流策略降速) ===
|
|
'--disable-background-networking',
|
|
'--disable-background-timer-throttling',
|
|
'--disable-renderer-backgrounding',
|
|
'--disable-backgrounding-occluded-windows',
|
|
|
|
// === 媒体与渲染优化 (针对视频发布场景) ===
|
|
'--enable-gpu',
|
|
'--enable-webgl',
|
|
'--ignore-gpu-blacklist',
|
|
'--enable-accelerated-video-decode',
|
|
'--enable-gpu-rasterization',
|
|
'--enable-proprietary-codecs', // 启用H.264等编解码器
|
|
|
|
// === 自动播放策略 ===
|
|
'--autoplay-policy=no-user-gesture-required',
|
|
|
|
// === 安全 ===
|
|
'--disable-features=IsolateOrigins,site-per-process',
|
|
];
|
|
|
|
/**
|
|
* 应该忽略的默认启动参数
|
|
* 移除 Chromium 的自动化标识,避免被平台检测
|
|
*/
|
|
export const BROWSER_IGNORE_DEFAULT_ARGS = [
|
|
'--enable-automation',
|
|
];
|
|
|
|
/**
|
|
* 反检测注入脚本
|
|
* 在每个页面加载前执行,隐藏 Playwright/webdriver 痕迹
|
|
*/
|
|
export const STEALTH_INIT_SCRIPT = `
|
|
// 隐藏 webdriver 标识
|
|
Object.defineProperty(navigator, 'webdriver', {
|
|
get: () => undefined,
|
|
configurable: true
|
|
});
|
|
|
|
// 伪造 chrome.runtime(完善方法)
|
|
window.chrome = {
|
|
runtime: {
|
|
PlatformOs: { MAC: 'mac', WIN: 'win', ANDROID: 'android', CROS: 'cros', LINUX: 'linux', OPENBSD: 'openbsd' },
|
|
PlatformArch: { ARM: 'arm', X86_32: 'x86-32', X86_64: 'x86-64', MIPS: 'mips', MIPS64: 'mips64' },
|
|
PlatformNaclArch: { ARM: 'arm', X86_32: 'x86-32', X86_64: 'x86-64', MIPS: 'mips', MIPS64: 'mips64' },
|
|
RequestUpdateCheckStatus: { THROTTLED: 'throttled', NO_UPDATE: 'no_update', UPDATE_AVAILABLE: 'update_available' },
|
|
OnInstalledReason: { INSTALL: 'install', UPDATE: 'update', CHROME_UPDATE: 'chrome_update', SHARED_MODULE_UPDATE: 'shared_module_update' },
|
|
OnRestartRequiredReason: { APP_UPDATE: 'app_update', OS_UPDATE: 'os_update', PERIODIC: 'periodic' },
|
|
connect: function() { return { onDisconnect: { addListener: function() {} }, onMessage: { addListener: function() {} }, postMessage: function() {}, disconnect: function() {} }; },
|
|
sendMessage: function() {},
|
|
id: undefined
|
|
},
|
|
loadTimes: function() { return { firstPaintTime: 0, startLoadTime: 0, commitLoadTime: 0, finishDocumentLoadTime: 0, finishLoadTime: 0, firstPaintAfterLoadTime: 0, navigationType: 'Other', requestTime: Date.now() / 1000 }; },
|
|
csi: function() { return { startE: Date.now(), onloadT: Date.now(), pageT: Math.random() * 1000 + 500, tran: 15 }; },
|
|
app: { isInstalled: false, InstallState: { DISABLED: 'disabled', INSTALLED: 'installed', NOT_INSTALLED: 'not_installed' }, RunningState: { CANNOT_RUN: 'cannot_run', READY_TO_RUN: 'ready_to_run', RUNNING: 'running' } },
|
|
};
|
|
|
|
// 伪造 navigator.plugins
|
|
Object.defineProperty(navigator, 'plugins', {
|
|
get: () => {
|
|
return [{
|
|
name: 'Chrome PDF Plugin',
|
|
description: 'Portable Document Format',
|
|
filename: 'internal-pdf-viewer',
|
|
length: 1,
|
|
}, {
|
|
name: 'Chrome PDF Viewer',
|
|
description: '',
|
|
filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai',
|
|
length: 1,
|
|
}, {
|
|
name: 'Native Client',
|
|
description: '',
|
|
filename: 'internal-nacl-plugin',
|
|
length: 2,
|
|
}];
|
|
},
|
|
configurable: true
|
|
});
|
|
|
|
// 伪造 navigator.languages
|
|
Object.defineProperty(navigator, 'languages', {
|
|
get: () => ['zh-CN', 'zh', 'en'],
|
|
configurable: true
|
|
});
|
|
|
|
// 伪造 navigator.hardwareConcurrency
|
|
Object.defineProperty(navigator, 'hardwareConcurrency', {
|
|
get: () => 8,
|
|
configurable: true
|
|
});
|
|
|
|
// 伪造 navigator.connection
|
|
if (!navigator.connection) {
|
|
Object.defineProperty(navigator, 'connection', {
|
|
get: () => ({ effectiveType: '4g', rtt: 50, downlink: 10, saveData: false }),
|
|
configurable: true
|
|
});
|
|
}
|
|
|
|
// 伪造 navigator.getBattery
|
|
if (!navigator.getBattery) {
|
|
navigator.getBattery = () => Promise.resolve({
|
|
charging: true,
|
|
chargingTime: 0,
|
|
dischargingTime: Infinity,
|
|
level: 1,
|
|
addEventListener: function() {},
|
|
removeEventListener: function() {},
|
|
dispatchEvent: function() { return true; }
|
|
});
|
|
}
|
|
|
|
// 清理 Playwright 特征
|
|
delete window.__playwright;
|
|
delete window.__pw_manual;
|
|
|
|
// 清理自动化工具特征
|
|
delete window._REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|
delete window.__selenium_unwrapped;
|
|
delete window.__webdriver_evaluate;
|
|
delete window.__selenium_evaluate;
|
|
delete window.__fxdriver_evaluate;
|
|
delete window.__driver_unwrapped;
|
|
delete window.__driver_evaluate;
|
|
delete window.__phantomas;
|
|
delete window.__nightmare;
|
|
delete window.callSelenium;
|
|
delete window._phantom;
|
|
delete window.callPhantom;
|
|
delete window.Buffer;
|
|
delete window.emit;
|
|
delete window.spawn;
|
|
delete window.webdriver;
|
|
`;
|