diff --git a/src/main/scripts/common/tabs.js b/src/main/scripts/common/tabs.js index 263970f..b10e4ee 100644 --- a/src/main/scripts/common/tabs.js +++ b/src/main/scripts/common/tabs.js @@ -1,4 +1,49 @@ -const preparePage = async (chromium, { tabIndex = Number(process.env.TAB_INDEX), endpoint = process.env.CDP_ENDPOINT || 'http://127.0.0.1:9222' } = {}) => { +const normalizeUrl = (value) => { + const raw = String(value || '').trim(); + if (!raw) return null; + try { + return new URL(raw); + } catch { + return null; + } +}; + +const isSameTarget = (currentUrl, targetUrl) => { + if (!currentUrl || !targetUrl) return false; + if (currentUrl.startsWith(targetUrl)) return true; + + const current = normalizeUrl(currentUrl); + const target = normalizeUrl(targetUrl); + if (!current || !target) return false; + if (current.origin !== target.origin) return false; + if (!current.pathname.startsWith(target.pathname)) return false; + return true; +}; + +const isSameOrigin = (currentUrl, targetUrl) => { + const current = normalizeUrl(currentUrl); + const target = normalizeUrl(targetUrl); + if (!current || !target) return false; + return current.origin === target.origin; +}; + +const isBlankLikePage = (url) => { + const u = String(url || '').trim().toLowerCase(); + if (!u) return true; + if (u === 'about:blank' || u.startsWith('about:blank#') || u.startsWith('about:blank?')) return true; + if (u === 'chrome://newtab/' || u.startsWith('chrome://newtab')) return true; + if (u.startsWith('chrome://new-tab-page')) return true; + return false; +}; + +const preparePage = async ( + chromium, + { + tabIndex = Number(process.env.TAB_INDEX), + endpoint = process.env.CDP_ENDPOINT || 'http://127.0.0.1:9222', + targetUrl, + } = {}, +) => { const browser = await chromium.connectOverCDP(endpoint); const context = browser.contexts()[0]; @@ -10,17 +55,67 @@ const preparePage = async (chromium, { tabIndex = Number(process.env.TAB_INDEX), Object.defineProperty(navigator, 'webdriver', { get: () => undefined }); }); - const idx = Number.isFinite(tabIndex) && tabIndex >= 0 ? Math.floor(tabIndex) : 0; let pages = await context.pages(); + let page = null; - while (pages.length <= idx) { - await context.newPage(); - pages = await context.pages(); + if (targetUrl) { + // 1. Try exact/path match + for (let i = 0; i < pages.length; i++) { + const p = pages[i]; + if (isSameTarget(p.url(), targetUrl)) { + page = p; + break; + } + } + + // 2. Try origin match + if (!page) { + for (let i = 0; i < pages.length; i++) { + const p = pages[i]; + if (isSameOrigin(p.url(), targetUrl)) { + page = p; + break; + } + } + } + } + + if (!page) { + const isTabIndexSet = Number.isFinite(tabIndex) && tabIndex >= 0; + + // If targetUrl provided and NO specific tabIndex requested, avoid hijacking tab 0 + if (targetUrl && !isTabIndexSet) { + // Try blank page + for (let i = 0; i < pages.length; i++) { + if (isBlankLikePage(pages[i].url())) { + page = pages[i]; + break; + } + } + // Else new page + if (!page) { + page = await context.newPage(); + } + } else { + // Legacy/Default behavior: use tabIndex (default 0) + const idx = isTabIndexSet ? Math.floor(tabIndex) : 0; + while (pages.length <= idx) { + await context.newPage(); + pages = await context.pages(); + } + page = pages[idx]; + } } - const page = pages[idx]; await page.bringToFront(); + if (targetUrl) { + const currentUrl = page.url(); + if (!currentUrl || !isSameTarget(currentUrl, targetUrl)) { + await page.goto(targetUrl, { waitUntil: 'domcontentloaded' }); + } + } + return { browser, context, page }; }; diff --git a/src/main/scripts/dy_hotel_trace.mjs b/src/main/scripts/dy_hotel_trace.mjs index 34a37af..2f67cb4 100644 --- a/src/main/scripts/dy_hotel_trace.mjs +++ b/src/main/scripts/dy_hotel_trace.mjs @@ -1,4 +1,22 @@ +import { chromium } from 'playwright'; import log from 'electron-log'; +import tabsPkg from './common/tabs.js'; -log.info('dy_trace.mjs placeholder: not implemented'); -process.exit(0); +const { preparePage, safeDisconnectBrowser } = tabsPkg; + +(async () => { + let browser; + + try { + const targetUrl = process.env.TARGET_URL || 'https://life.douyin.com/'; + const prepared = await preparePage(chromium, { targetUrl }); + browser = prepared.browser; + + log.info('dy_hotel_trace.mjs placeholder: not implemented'); + } catch (error) { + log.error(error); + process.exitCode = 1; + } finally { + await safeDisconnectBrowser(browser); + } +})(); diff --git a/src/main/scripts/fg_trace.mjs b/src/main/scripts/fg_trace.mjs index ff290b5..c0833b5 100644 --- a/src/main/scripts/fg_trace.mjs +++ b/src/main/scripts/fg_trace.mjs @@ -1,6 +1,6 @@ import { chromium } from 'playwright'; import log from 'electron-log'; -import tabsPkg from './common/tabs'; +import tabsPkg from './common/tabs.js'; const { preparePage, safeDisconnectBrowser } = tabsPkg; @@ -94,16 +94,11 @@ const toggleRoomByDateIndex = async (container, { roomType, dateIndex, operation let browser; try { - const prepared = await preparePage(chromium); + const targetUrl = 'https://hotel.fliggy.com/ebooking/hotelBaseInfoUv.htm#/ebk/homeV1'; + const prepared = await preparePage(chromium, { targetUrl }); browser = prepared.browser; const page = prepared.page; - const targetUrl = 'https://hotel.fliggy.com/ebooking/hotelBaseInfoUv.htm#/ebk/homeV1'; - const currentUrl = page.url(); - if (!currentUrl || !currentUrl.startsWith(targetUrl)) { - await page.goto(targetUrl); - } - await page.waitForTimeout(4000 + Math.random() * 300); await page.getByRole('menuitem', { name: '房价房量管理' }).click(); diff --git a/src/main/scripts/mt_trace.mjs b/src/main/scripts/mt_trace.mjs index 6ddd4b7..bcdac99 100644 --- a/src/main/scripts/mt_trace.mjs +++ b/src/main/scripts/mt_trace.mjs @@ -7,14 +7,10 @@ const { preparePage, safeDisconnectBrowser } = tabsPkg; let browser; try { - const prepared = await preparePage(chromium); + const targetUrl = 'https://me.meituan.com/ebooking/merchant/product#/index'; + const prepared = await preparePage(chromium, { targetUrl }); browser = prepared.browser; const page = prepared.page; - const targetUrl = 'https://me.meituan.com/ebooking/merchant/product#/index'; - const currentUrl = page.url(); - if (!currentUrl || !currentUrl.startsWith(targetUrl)) { - await page.goto(targetUrl); - } } catch (error) { log.error(error); process.exitCode = 1; diff --git a/src/main/scripts/open_all_channel.mjs b/src/main/scripts/open_all_channel.mjs index ba2e6f4..74b651e 100644 --- a/src/main/scripts/open_all_channel.mjs +++ b/src/main/scripts/open_all_channel.mjs @@ -12,6 +12,37 @@ const parseChannels = () => { } }; +const normalizeUrl = (value) => { + const raw = String(value || '').trim(); + if (!raw) return null; + try { + return new URL(raw); + } catch { + return null; + } +}; + +const isSameTarget = (currentUrl, targetUrl) => { + if (!currentUrl || !targetUrl) return false; + if (currentUrl.startsWith(targetUrl)) return true; + + const current = normalizeUrl(currentUrl); + const target = normalizeUrl(targetUrl); + if (!current || !target) return false; + if (current.origin !== target.origin) return false; + if (!current.pathname.startsWith(target.pathname)) return false; + return true; +}; + +const isBlankLikePage = (url) => { + const u = String(url || '').trim().toLowerCase(); + if (!u) return true; + if (u === 'about:blank' || u.startsWith('about:blank#') || u.startsWith('about:blank?')) return true; + if (u === 'chrome://newtab/' || u.startsWith('chrome://newtab')) return true; + if (u.startsWith('chrome://new-tab-page')) return true; + return false; +}; + (async () => { let browser; @@ -34,27 +65,56 @@ const parseChannels = () => { Object.defineProperty(navigator, 'webdriver', { get: () => undefined }); }); + const usedPages = new Set(); + for (let i = 0; i < channels.length; i++) { const targetUrl = channels[i]?.channelUrl; if (!targetUrl) continue; let pages = await context.pages(); - - while (pages.length <= i) { - await context.newPage(); - pages = await context.pages(); + let page = null; + for (let j = 0; j < pages.length; j++) { + const p = pages[j]; + const current = p.url(); + if (isSameTarget(current, targetUrl)) { + page = p; + break; + } } - const page = pages[i]; + if (!page) { + for (let j = 0; j < pages.length; j++) { + const p = pages[j]; + if (usedPages.has(p)) continue; + if (isBlankLikePage(p.url())) { + page = p; + break; + } + } + } + + if (!page) { + page = await context.newPage(); + } await page.bringToFront(); + usedPages.add(page); const current = page.url(); - if (!current || !current.startsWith(targetUrl)) { + if (!current || !isSameTarget(current, targetUrl)) { await page.goto(targetUrl, { waitUntil: 'domcontentloaded' }); } } + + const pagesAfter = await context.pages(); + for (const p of pagesAfter) { + if (!usedPages.has(p) && isBlankLikePage(p.url())) { + try { + await p.close(); + } catch {} + } + } } catch (error) { log.error(error); process.exitCode = 1;