多个A2C任务时防止同时同步引起错误
This commit is contained in:
+171
-13
@@ -514,16 +514,24 @@ async function ensureCaptchaProxyBrowserIfNeeded(browserCtx) {
|
||||
extraArgs,
|
||||
sessionKey: antiBot.sessionKey || '',
|
||||
});
|
||||
tagBrowserSession(browserSession, profile, antiBot.sessionKey || '', extraArgs);
|
||||
browserSession.captchaProxyActive = true;
|
||||
|
||||
const proxySessionRef = { current: browserSession };
|
||||
page = await createTaskPage(
|
||||
browserSession.browser,
|
||||
browserSession.reused ? null : browserSession.page,
|
||||
navigationUrl,
|
||||
antiBot,
|
||||
mergedCookies.length > 0 ? mergedCookies : snapshotCookies,
|
||||
{ ...pageExtraOpts, useCaptchaProxy: true }
|
||||
{
|
||||
...pageExtraOpts,
|
||||
useCaptchaProxy: true,
|
||||
browserSession,
|
||||
browserSessionRef: proxySessionRef,
|
||||
}
|
||||
);
|
||||
browserSession = proxySessionRef.current;
|
||||
|
||||
if (antiBot?.enabled) {
|
||||
attachSitekeyCapture(page, browserSession.browser);
|
||||
@@ -541,28 +549,95 @@ async function ensureCaptchaProxyBrowserIfNeeded(browserCtx) {
|
||||
browserCtx.page = page;
|
||||
}
|
||||
|
||||
/** 任务结束关闭本次 Tab(温池复用 Browser 时保留 Browser 实例) */
|
||||
/**
|
||||
* 为温池复用 / newPage 重试附加元数据(profile、sessionKey、启动参数)
|
||||
* @param {object} browserSession
|
||||
* @param {'standard'|'real'} profile
|
||||
* @param {string} sessionKey
|
||||
* @param {string[]} launchExtraArgs
|
||||
*/
|
||||
function tagBrowserSession(browserSession, profile, sessionKey, launchExtraArgs = []) {
|
||||
browserSession.profile = profile;
|
||||
browserSession.sessionKey = sessionKey || '';
|
||||
browserSession.launchExtraArgs = launchExtraArgs;
|
||||
return browserSession;
|
||||
}
|
||||
|
||||
/** @param {Error|unknown} err */
|
||||
function isNewTabOpenError(err) {
|
||||
const msg = String(err?.message || err || '').toLowerCase();
|
||||
return msg.includes('target.createtarget') || msg.includes('failed to open a new tab');
|
||||
}
|
||||
|
||||
/**
|
||||
* 温池 Browser 若已无任何 Tab,销毁并冷启动(避免后续 newPage 失败)
|
||||
* @param {object|null} browserSession
|
||||
*/
|
||||
async function relaunchBrowserSessionIfNoTabs(browserSession) {
|
||||
if (!browserSession?.pooled || !browserSession.browser) {
|
||||
return browserSession;
|
||||
}
|
||||
let pages = [];
|
||||
try {
|
||||
pages = await browserSession.browser.pages();
|
||||
} catch (_) {
|
||||
pages = [];
|
||||
}
|
||||
if (pages.length > 0) {
|
||||
return browserSession;
|
||||
}
|
||||
|
||||
console.warn('[createTaskPage] 温池 Browser pages=0,销毁并冷启动');
|
||||
await browserSession.release(true);
|
||||
const relaunched = await acquireBrowserSession({
|
||||
profile: browserSession.profile || 'real',
|
||||
extraArgs: browserSession.launchExtraArgs || ['--mute-audio'],
|
||||
sessionKey: browserSession.sessionKey || '',
|
||||
});
|
||||
tagBrowserSession(
|
||||
relaunched,
|
||||
browserSession.profile || relaunched.profile || 'real',
|
||||
browserSession.sessionKey || '',
|
||||
browserSession.launchExtraArgs || ['--mute-audio']
|
||||
);
|
||||
return relaunched;
|
||||
}
|
||||
|
||||
/** 任务结束关闭本次 Tab(温池复用 Browser 时保留至少一个 Tab) */
|
||||
async function closeTaskPage(page) {
|
||||
if (!page) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (typeof page.isClosed === 'function' && !page.isClosed()) {
|
||||
await page.close();
|
||||
if (typeof page.isClosed === 'function' && page.isClosed()) {
|
||||
return;
|
||||
}
|
||||
const browser = typeof page.browser === 'function' ? page.browser() : null;
|
||||
if (browser) {
|
||||
const pages = await browser.pages().catch(() => []);
|
||||
if (pages.length <= 1) {
|
||||
await page.goto('about:blank', {
|
||||
waitUntil: 'domcontentloaded',
|
||||
timeout: Math.min(NAVIGATION_TIMEOUT_MS, 15000),
|
||||
}).catch(() => {});
|
||||
return;
|
||||
}
|
||||
}
|
||||
await page.close();
|
||||
} catch (_) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务页创建(含 Whatshub skipUserAgent)
|
||||
* 温池复用 Browser 时每次 newPage,冷启动 Real Browser 可使用 launch 返回的首个 Tab
|
||||
* 配置任务页(UA / Cookie / 事件监听)
|
||||
* @param {import('puppeteer').Page} page
|
||||
* @param {import('puppeteer').Browser} browser
|
||||
* @param {object} opts
|
||||
* @param {object} extraOpts
|
||||
* @param {object|null|undefined} antiBot
|
||||
*/
|
||||
async function createTaskPage(browser, warmPage, pageUrl, antiBot, mergedCookies, extraOpts = {}) {
|
||||
const opts = buildManagedPageOptions(pageUrl, antiBot, mergedCookies, extraOpts);
|
||||
const useWarmPage = !!(warmPage && extraOpts.useWarmPage !== false && !extraOpts.forceNewPage);
|
||||
const page = useWarmPage ? warmPage : await browser.newPage();
|
||||
async function configureTaskPage(page, browser, opts, extraOpts, antiBot) {
|
||||
page.setDefaultNavigationTimeout(NAVIGATION_TIMEOUT_MS);
|
||||
page.setDefaultTimeout(PAGE_DEFAULT_TIMEOUT_MS);
|
||||
|
||||
@@ -586,7 +661,67 @@ async function createTaskPage(browser, warmPage, pageUrl, antiBot, mergedCookies
|
||||
if (antiBot?.enabled) {
|
||||
attachSitekeyCapture(page, browser);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务页创建(含 Whatshub skipUserAgent)
|
||||
* 温池复用 Browser 时每次 newPage;失败则销毁温池条目并冷启动重试
|
||||
*
|
||||
* extraOpts.browserSession:传入时可触发 pages=0 重建与 newPage 失败重试
|
||||
* extraOpts.browserSessionRef:可选 { current },重试后回写最新 session
|
||||
*/
|
||||
async function createTaskPage(browser, warmPage, pageUrl, antiBot, mergedCookies, extraOpts = {}) {
|
||||
const opts = buildManagedPageOptions(pageUrl, antiBot, mergedCookies, extraOpts);
|
||||
let browserSession = extraOpts.browserSession || null;
|
||||
let currentBrowser = browser;
|
||||
let currentWarmPage = warmPage;
|
||||
|
||||
if (browserSession) {
|
||||
browserSession = await relaunchBrowserSessionIfNoTabs(browserSession);
|
||||
currentBrowser = browserSession.browser;
|
||||
currentWarmPage = browserSession.reused ? null : browserSession.page;
|
||||
if (extraOpts.browserSessionRef) {
|
||||
extraOpts.browserSessionRef.current = browserSession;
|
||||
}
|
||||
}
|
||||
|
||||
let page = null;
|
||||
for (let attempt = 0; attempt < 2; attempt++) {
|
||||
const useWarmPage = !!(currentWarmPage && extraOpts.useWarmPage !== false && !extraOpts.forceNewPage);
|
||||
try {
|
||||
page = useWarmPage ? currentWarmPage : await currentBrowser.newPage();
|
||||
break;
|
||||
} catch (err) {
|
||||
if (attempt === 0 && browserSession && isNewTabOpenError(err)) {
|
||||
console.warn('[createTaskPage] newPage 失败,销毁温池并冷启动重试:', err.message);
|
||||
await browserSession.release(true);
|
||||
browserSession = await acquireBrowserSession({
|
||||
profile: browserSession.profile || (antiBot?.profile === 'real' ? 'real' : 'standard'),
|
||||
extraArgs: browserSession.launchExtraArgs || ['--mute-audio'],
|
||||
sessionKey: browserSession.sessionKey || antiBot?.sessionKey || '',
|
||||
});
|
||||
tagBrowserSession(
|
||||
browserSession,
|
||||
browserSession.profile || (antiBot?.profile === 'real' ? 'real' : 'standard'),
|
||||
browserSession.sessionKey || antiBot?.sessionKey || '',
|
||||
browserSession.launchExtraArgs || ['--mute-audio']
|
||||
);
|
||||
if (extraOpts.browserSessionRef) {
|
||||
extraOpts.browserSessionRef.current = browserSession;
|
||||
}
|
||||
currentBrowser = browserSession.browser;
|
||||
currentWarmPage = browserSession.reused ? null : browserSession.page;
|
||||
continue;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
if (!page) {
|
||||
throw new Error('createTaskPage: 未能打开任务 Tab');
|
||||
}
|
||||
|
||||
await configureTaskPage(page, currentBrowser, opts, extraOpts, antiBot);
|
||||
return page;
|
||||
}
|
||||
|
||||
@@ -1554,18 +1689,22 @@ app.post('/api/auth-and-intercept', async (req, res) => {
|
||||
extraArgs: ['--mute-audio'],
|
||||
sessionKey: antiBot.sessionKey || '',
|
||||
});
|
||||
tagBrowserSession(browserSession, profile, antiBot.sessionKey || '', ['--mute-audio']);
|
||||
cfLog.browserReused = !!browserSession.reused;
|
||||
|
||||
const { storedSession, mergedCookies } = resolveSessionContext(antiBot, pageUrl);
|
||||
const navigationUrl = resolveNavigationUrl(pageUrl, antiBot);
|
||||
|
||||
const authSessionRef = { current: browserSession };
|
||||
page = await createTaskPage(
|
||||
browserSession.browser,
|
||||
browserSession.reused ? null : browserSession.page,
|
||||
navigationUrl,
|
||||
antiBot,
|
||||
mergedCookies
|
||||
mergedCookies,
|
||||
{ browserSession, browserSessionRef: authSessionRef }
|
||||
);
|
||||
browserSession = authSessionRef.current;
|
||||
|
||||
const httpResolvedUrl = await resolveHttpRedirects(pageUrl).catch(() => pageUrl);
|
||||
if (httpResolvedUrl !== pageUrl) {
|
||||
@@ -1840,17 +1979,24 @@ app.post('/api/ui-pagination', async (req, res) => {
|
||||
extraArgs: ['--window-size=1920,1080'],
|
||||
sessionKey: antiBot.sessionKey || '',
|
||||
});
|
||||
tagBrowserSession(browserSession, profile, antiBot.sessionKey || '', ['--window-size=1920,1080']);
|
||||
|
||||
const { storedSession, mergedCookies } = resolveSessionContext(antiBot, navigationUrl, cookies || []);
|
||||
|
||||
const uiSessionRef = { current: browserSession };
|
||||
page = await createTaskPage(
|
||||
browserSession.browser,
|
||||
browserSession.reused ? null : browserSession.page,
|
||||
navigationUrl,
|
||||
antiBot,
|
||||
mergedCookies,
|
||||
{ viewport: { width: 1920, height: 1080 } }
|
||||
{
|
||||
viewport: { width: 1920, height: 1080 },
|
||||
browserSession,
|
||||
browserSessionRef: uiSessionRef,
|
||||
}
|
||||
);
|
||||
browserSession = uiSessionRef.current;
|
||||
|
||||
const log = (msg) => { console.log(msg); debugLogs.push(msg); };
|
||||
log(`[启动] 准备访问页面: ${navigationUrl}`);
|
||||
@@ -1986,19 +2132,31 @@ app.post('/api/auth-intercept-and-paginate', async (req, res) => {
|
||||
extraArgs: ['--mute-audio', '--window-size=1920,1080'],
|
||||
sessionKey: antiBot.sessionKey || '',
|
||||
});
|
||||
tagBrowserSession(
|
||||
browserSession,
|
||||
profile,
|
||||
antiBot.sessionKey || '',
|
||||
['--mute-audio', '--window-size=1920,1080']
|
||||
);
|
||||
cfLog.browserReused = !!browserSession.reused;
|
||||
|
||||
const { storedSession, mergedCookies } = resolveSessionContext(antiBot, pageUrl);
|
||||
const navigationUrl = resolveNavigationUrl(pageUrl, antiBot);
|
||||
|
||||
const mergedSessionRef = { current: browserSession };
|
||||
page = await createTaskPage(
|
||||
browserSession.browser,
|
||||
browserSession.reused ? null : browserSession.page,
|
||||
navigationUrl,
|
||||
antiBot,
|
||||
mergedCookies,
|
||||
{ viewport: { width: 1920, height: 1080 } }
|
||||
{
|
||||
viewport: { width: 1920, height: 1080 },
|
||||
browserSession,
|
||||
browserSessionRef: mergedSessionRef,
|
||||
}
|
||||
);
|
||||
browserSession = mergedSessionRef.current;
|
||||
|
||||
const httpResolvedUrl = await resolveHttpRedirects(pageUrl).catch(() => pageUrl);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user