|
@@ -1,627 +0,0 @@
|
|
|
-"use strict";
|
|
|
|
|
-const { app, BrowserWindow, ipcMain, shell, session, Menu, Tray, nativeImage, webContents } = require("electron");
|
|
|
|
|
-const { join } = require("path");
|
|
|
|
|
-require("fs");
|
|
|
|
|
-let mainWindow = null;
|
|
|
|
|
-let tray = null;
|
|
|
|
|
-let isQuitting = false;
|
|
|
|
|
-const VITE_DEV_SERVER_URL = process.env.VITE_DEV_SERVER_URL;
|
|
|
|
|
-function getIconPath() {
|
|
|
|
|
- return VITE_DEV_SERVER_URL ? join(__dirname, "../public/icons/icon-256.png") : join(__dirname, "../dist/icons/icon-256.png");
|
|
|
|
|
-}
|
|
|
|
|
-function getTrayIconPath() {
|
|
|
|
|
- return VITE_DEV_SERVER_URL ? join(__dirname, "../public/icons/tray-icon.png") : join(__dirname, "../dist/icons/tray-icon.png");
|
|
|
|
|
-}
|
|
|
|
|
-function createTrayIcon() {
|
|
|
|
|
- const trayIconPath = getTrayIconPath();
|
|
|
|
|
- return nativeImage.createFromPath(trayIconPath);
|
|
|
|
|
-}
|
|
|
|
|
-function createTray() {
|
|
|
|
|
- const trayIcon = createTrayIcon();
|
|
|
|
|
- tray = new Tray(trayIcon);
|
|
|
|
|
- const contextMenu = Menu.buildFromTemplate([
|
|
|
|
|
- {
|
|
|
|
|
- label: "显示主窗口",
|
|
|
|
|
- click: () => {
|
|
|
|
|
- if (mainWindow) {
|
|
|
|
|
- mainWindow.show();
|
|
|
|
|
- mainWindow.focus();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- label: "最小化到托盘",
|
|
|
|
|
- click: () => {
|
|
|
|
|
- mainWindow == null ? void 0 : mainWindow.hide();
|
|
|
|
|
- }
|
|
|
|
|
- },
|
|
|
|
|
- { type: "separator" },
|
|
|
|
|
- {
|
|
|
|
|
- label: "退出",
|
|
|
|
|
- click: () => {
|
|
|
|
|
- isQuitting = true;
|
|
|
|
|
- app.quit();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- ]);
|
|
|
|
|
- tray.setToolTip("多平台媒体管理系统");
|
|
|
|
|
- tray.setContextMenu(contextMenu);
|
|
|
|
|
- tray.on("click", () => {
|
|
|
|
|
- if (mainWindow) {
|
|
|
|
|
- if (mainWindow.isVisible()) {
|
|
|
|
|
- mainWindow.focus();
|
|
|
|
|
- } else {
|
|
|
|
|
- mainWindow.show();
|
|
|
|
|
- mainWindow.focus();
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
- tray.on("double-click", () => {
|
|
|
|
|
- if (mainWindow) {
|
|
|
|
|
- mainWindow.show();
|
|
|
|
|
- mainWindow.focus();
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
-}
|
|
|
|
|
-function createWindow() {
|
|
|
|
|
- Menu.setApplicationMenu(null);
|
|
|
|
|
- const iconPath = getIconPath();
|
|
|
|
|
- mainWindow = new BrowserWindow({
|
|
|
|
|
- width: 1400,
|
|
|
|
|
- height: 900,
|
|
|
|
|
- minWidth: 1200,
|
|
|
|
|
- minHeight: 700,
|
|
|
|
|
- icon: iconPath,
|
|
|
|
|
- webPreferences: {
|
|
|
|
|
- preload: join(__dirname, "preload.js"),
|
|
|
|
|
- nodeIntegration: false,
|
|
|
|
|
- contextIsolation: true,
|
|
|
|
|
- webviewTag: true
|
|
|
|
|
- // 启用 webview 标签
|
|
|
|
|
- },
|
|
|
|
|
- frame: false,
|
|
|
|
|
- // 无边框窗口,自定义标题栏
|
|
|
|
|
- transparent: false,
|
|
|
|
|
- backgroundColor: "#f0f2f5",
|
|
|
|
|
- show: false
|
|
|
|
|
- });
|
|
|
|
|
- mainWindow.once("ready-to-show", () => {
|
|
|
|
|
- mainWindow == null ? void 0 : mainWindow.show();
|
|
|
|
|
- setupWindowEvents();
|
|
|
|
|
- });
|
|
|
|
|
- if (VITE_DEV_SERVER_URL) {
|
|
|
|
|
- mainWindow.loadURL(VITE_DEV_SERVER_URL);
|
|
|
|
|
- mainWindow.webContents.openDevTools();
|
|
|
|
|
- } else {
|
|
|
|
|
- mainWindow.loadFile(join(__dirname, "../dist/index.html"));
|
|
|
|
|
- }
|
|
|
|
|
- mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
|
|
|
|
- shell.openExternal(url);
|
|
|
|
|
- return { action: "deny" };
|
|
|
|
|
- });
|
|
|
|
|
- mainWindow.on("close", (event) => {
|
|
|
|
|
- if (!isQuitting) {
|
|
|
|
|
- event.preventDefault();
|
|
|
|
|
- mainWindow == null ? void 0 : mainWindow.hide();
|
|
|
|
|
- if (tray && !app.isPackaged) ;
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
- mainWindow.on("closed", () => {
|
|
|
|
|
- mainWindow = null;
|
|
|
|
|
- });
|
|
|
|
|
-}
|
|
|
|
|
-const gotTheLock = app.requestSingleInstanceLock();
|
|
|
|
|
-if (!gotTheLock) {
|
|
|
|
|
- app.quit();
|
|
|
|
|
-} else {
|
|
|
|
|
- app.on("second-instance", () => {
|
|
|
|
|
- if (mainWindow) {
|
|
|
|
|
- mainWindow.show();
|
|
|
|
|
- if (mainWindow.isMinimized()) mainWindow.restore();
|
|
|
|
|
- mainWindow.focus();
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
- app.whenReady().then(() => {
|
|
|
|
|
- createTray();
|
|
|
|
|
- createWindow();
|
|
|
|
|
- setupWebviewSessions();
|
|
|
|
|
- app.on("activate", () => {
|
|
|
|
|
- if (BrowserWindow.getAllWindows().length === 0) {
|
|
|
|
|
- createWindow();
|
|
|
|
|
- } else if (mainWindow) {
|
|
|
|
|
- mainWindow.show();
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
- });
|
|
|
|
|
-}
|
|
|
|
|
-function setupWebviewSessions() {
|
|
|
|
|
- app.on("web-contents-created", (_event, contents) => {
|
|
|
|
|
- if (contents.getType() === "webview") {
|
|
|
|
|
- contents.setUserAgent(
|
|
|
|
|
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
|
|
|
|
- );
|
|
|
|
|
- contents.on("will-navigate", (event, url) => {
|
|
|
|
|
- if (!isAllowedUrl(url)) {
|
|
|
|
|
- console.log("[WebView] 阻止导航到自定义协议:", url);
|
|
|
|
|
- event.preventDefault();
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
- contents.setWindowOpenHandler(({ url }) => {
|
|
|
|
|
- if (!isAllowedUrl(url)) {
|
|
|
|
|
- console.log("[WebView] 阻止打开自定义协议窗口:", url);
|
|
|
|
|
- return { action: "deny" };
|
|
|
|
|
- }
|
|
|
|
|
- console.log("[WebView] 拦截新窗口,在当前页面打开:", url);
|
|
|
|
|
- contents.loadURL(url);
|
|
|
|
|
- return { action: "deny" };
|
|
|
|
|
- });
|
|
|
|
|
- contents.session.setPermissionRequestHandler((_webContents, permission, callback) => {
|
|
|
|
|
- callback(true);
|
|
|
|
|
- });
|
|
|
|
|
- contents.session.webRequest.onBeforeSendHeaders((details, callback) => {
|
|
|
|
|
- delete details.requestHeaders["X-DevTools-Emulate-Network-Conditions-Client-Id"];
|
|
|
|
|
- if (!details.requestHeaders["Origin"] && !details.requestHeaders["origin"]) ;
|
|
|
|
|
- callback({ requestHeaders: details.requestHeaders });
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
-}
|
|
|
|
|
-function isAllowedUrl(url) {
|
|
|
|
|
- if (!url) return false;
|
|
|
|
|
- const lowerUrl = url.toLowerCase();
|
|
|
|
|
- return lowerUrl.startsWith("http://") || lowerUrl.startsWith("https://") || lowerUrl.startsWith("about:") || lowerUrl.startsWith("data:");
|
|
|
|
|
-}
|
|
|
|
|
-app.on("window-all-closed", () => {
|
|
|
|
|
-});
|
|
|
|
|
-app.on("before-quit", () => {
|
|
|
|
|
- isQuitting = true;
|
|
|
|
|
-});
|
|
|
|
|
-app.on("quit", () => {
|
|
|
|
|
- if (tray) {
|
|
|
|
|
- tray.destroy();
|
|
|
|
|
- tray = null;
|
|
|
|
|
- }
|
|
|
|
|
-});
|
|
|
|
|
-ipcMain.handle("get-app-version", () => {
|
|
|
|
|
- return app.getVersion();
|
|
|
|
|
-});
|
|
|
|
|
-ipcMain.handle("get-platform", () => {
|
|
|
|
|
- return process.platform;
|
|
|
|
|
-});
|
|
|
|
|
-ipcMain.on("window-minimize", () => {
|
|
|
|
|
- mainWindow == null ? void 0 : mainWindow.minimize();
|
|
|
|
|
-});
|
|
|
|
|
-ipcMain.on("window-maximize", () => {
|
|
|
|
|
- if (mainWindow == null ? void 0 : mainWindow.isMaximized()) {
|
|
|
|
|
- mainWindow.unmaximize();
|
|
|
|
|
- } else {
|
|
|
|
|
- mainWindow == null ? void 0 : mainWindow.maximize();
|
|
|
|
|
- }
|
|
|
|
|
-});
|
|
|
|
|
-ipcMain.on("window-close", () => {
|
|
|
|
|
- mainWindow == null ? void 0 : mainWindow.hide();
|
|
|
|
|
-});
|
|
|
|
|
-ipcMain.on("app-quit", () => {
|
|
|
|
|
- isQuitting = true;
|
|
|
|
|
- app.quit();
|
|
|
|
|
-});
|
|
|
|
|
-ipcMain.handle("window-is-maximized", () => {
|
|
|
|
|
- return (mainWindow == null ? void 0 : mainWindow.isMaximized()) || false;
|
|
|
|
|
-});
|
|
|
|
|
-function setupWindowEvents() {
|
|
|
|
|
- mainWindow == null ? void 0 : mainWindow.on("maximize", () => {
|
|
|
|
|
- mainWindow == null ? void 0 : mainWindow.webContents.send("window-maximized", true);
|
|
|
|
|
- });
|
|
|
|
|
- mainWindow == null ? void 0 : mainWindow.on("unmaximize", () => {
|
|
|
|
|
- mainWindow == null ? void 0 : mainWindow.webContents.send("window-maximized", false);
|
|
|
|
|
- });
|
|
|
|
|
-}
|
|
|
|
|
-ipcMain.handle("open-backend-external", async (_event, payload) => {
|
|
|
|
|
- const { url, cookieData, title } = payload || {};
|
|
|
|
|
- if (!url || typeof url !== "string") return;
|
|
|
|
|
- const partition = "persist:backend-popup-" + Date.now();
|
|
|
|
|
- const ses = session.fromPartition(partition);
|
|
|
|
|
- if (cookieData && typeof cookieData === "string" && cookieData.trim()) {
|
|
|
|
|
- const raw = cookieData.trim();
|
|
|
|
|
- let cookiesToSet = [];
|
|
|
|
|
- try {
|
|
|
|
|
- if (raw.startsWith("[") || raw.startsWith("{")) {
|
|
|
|
|
- const parsed = JSON.parse(raw);
|
|
|
|
|
- const arr = Array.isArray(parsed) ? parsed : (parsed == null ? void 0 : parsed.cookies) || [];
|
|
|
|
|
- cookiesToSet = arr.map((c) => ({
|
|
|
|
|
- name: String((c == null ? void 0 : c.name) ?? "").trim(),
|
|
|
|
|
- value: String((c == null ? void 0 : c.value) ?? "").trim(),
|
|
|
|
|
- domain: (c == null ? void 0 : c.domain) ? String(c.domain) : void 0,
|
|
|
|
|
- path: (c == null ? void 0 : c.path) ? String(c.path) : "/"
|
|
|
|
|
- })).filter((c) => c.name);
|
|
|
|
|
- } else {
|
|
|
|
|
- raw.split(";").forEach((p) => {
|
|
|
|
|
- const idx = p.indexOf("=");
|
|
|
|
|
- if (idx > 0) {
|
|
|
|
|
- const name = p.slice(0, idx).trim();
|
|
|
|
|
- const value = p.slice(idx + 1).trim();
|
|
|
|
|
- if (name) cookiesToSet.push({ name, value, path: "/" });
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
- } catch (e) {
|
|
|
|
|
- console.warn("[open-backend-external] 解析 cookie 失败", e);
|
|
|
|
|
- }
|
|
|
|
|
- const origin = new URL(url).origin;
|
|
|
|
|
- const hostname = new URL(url).hostname;
|
|
|
|
|
- const defaultDomain = hostname.startsWith("www.") ? hostname.slice(4) : hostname;
|
|
|
|
|
- const domainWithDot = defaultDomain.includes(".") ? "." + defaultDomain.split(".").slice(-2).join(".") : void 0;
|
|
|
|
|
- for (const c of cookiesToSet) {
|
|
|
|
|
- try {
|
|
|
|
|
- await ses.cookies.set({
|
|
|
|
|
- url: origin + "/",
|
|
|
|
|
- name: c.name,
|
|
|
|
|
- value: c.value,
|
|
|
|
|
- domain: c.domain || domainWithDot || hostname,
|
|
|
|
|
- path: c.path || "/"
|
|
|
|
|
- });
|
|
|
|
|
- } catch (err) {
|
|
|
|
|
- console.warn("[open-backend-external] 设置 cookie 失败", c.name, err);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- const win = new BrowserWindow({
|
|
|
|
|
- width: 1280,
|
|
|
|
|
- height: 800,
|
|
|
|
|
- title: title || "平台后台",
|
|
|
|
|
- icon: getIconPath(),
|
|
|
|
|
- webPreferences: {
|
|
|
|
|
- session: ses,
|
|
|
|
|
- nodeIntegration: false,
|
|
|
|
|
- contextIsolation: true
|
|
|
|
|
- },
|
|
|
|
|
- show: false
|
|
|
|
|
- });
|
|
|
|
|
- win.once("ready-to-show", () => {
|
|
|
|
|
- win.show();
|
|
|
|
|
- });
|
|
|
|
|
- await win.loadURL(url);
|
|
|
|
|
- return { ok: true };
|
|
|
|
|
-});
|
|
|
|
|
-ipcMain.handle("get-webview-cookies", async (_event, partition, url) => {
|
|
|
|
|
- try {
|
|
|
|
|
- const ses = session.fromPartition(partition);
|
|
|
|
|
- const cookies = await ses.cookies.get({ url });
|
|
|
|
|
- return cookies;
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error("获取 cookies 失败:", error);
|
|
|
|
|
- return [];
|
|
|
|
|
- }
|
|
|
|
|
-});
|
|
|
|
|
-ipcMain.handle("clear-webview-cookies", async (_event, partition) => {
|
|
|
|
|
- try {
|
|
|
|
|
- const ses = session.fromPartition(partition);
|
|
|
|
|
- await ses.clearStorageData({ storages: ["cookies"] });
|
|
|
|
|
- return true;
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error("清除 cookies 失败:", error);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-});
|
|
|
|
|
-ipcMain.handle("set-webview-cookies", async (_event, partition, cookies) => {
|
|
|
|
|
- try {
|
|
|
|
|
- console.log(`[Main] 设置 webview cookies, partition=${partition}, count=${cookies.length}`);
|
|
|
|
|
- const ses = session.fromPartition(partition);
|
|
|
|
|
- let successCount = 0;
|
|
|
|
|
- for (const cookie of cookies) {
|
|
|
|
|
- try {
|
|
|
|
|
- const cookieToSet = {
|
|
|
|
|
- url: cookie.url,
|
|
|
|
|
- name: cookie.name,
|
|
|
|
|
- value: cookie.value,
|
|
|
|
|
- domain: cookie.domain,
|
|
|
|
|
- path: cookie.path || "/"
|
|
|
|
|
- };
|
|
|
|
|
- if (cookie.expirationDate) {
|
|
|
|
|
- cookieToSet.expirationDate = cookie.expirationDate;
|
|
|
|
|
- }
|
|
|
|
|
- if (cookie.httpOnly !== void 0) {
|
|
|
|
|
- cookieToSet.httpOnly = cookie.httpOnly;
|
|
|
|
|
- }
|
|
|
|
|
- if (cookie.secure !== void 0) {
|
|
|
|
|
- cookieToSet.secure = cookie.secure;
|
|
|
|
|
- }
|
|
|
|
|
- if (cookie.sameSite) {
|
|
|
|
|
- cookieToSet.sameSite = cookie.sameSite;
|
|
|
|
|
- }
|
|
|
|
|
- await ses.cookies.set(cookieToSet);
|
|
|
|
|
- successCount++;
|
|
|
|
|
- if (cookie.name === "BDUSS" || cookie.name === "STOKEN" || cookie.name === "sessionid") {
|
|
|
|
|
- console.log(`[Main] 成功设置关键 Cookie: ${cookie.name}, domain: ${cookie.domain}`);
|
|
|
|
|
- }
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error(`[Main] 设置 cookie 失败 (${cookie.name}):`, error);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- console.log(`[Main] 成功设置 ${successCount}/${cookies.length} 个 cookies`);
|
|
|
|
|
- try {
|
|
|
|
|
- const setCookies = await ses.cookies.get({ domain: ".baidu.com" });
|
|
|
|
|
- console.log(`[Main] 验证:当前 session 中有 ${setCookies.length} 个百度 Cookie`);
|
|
|
|
|
- const keyNames = setCookies.slice(0, 5).map((c) => c.name).join(", ");
|
|
|
|
|
- console.log(`[Main] 关键 Cookie 名称: ${keyNames}`);
|
|
|
|
|
- } catch (verifyError) {
|
|
|
|
|
- console.error("[Main] 验证 Cookie 失败:", verifyError);
|
|
|
|
|
- }
|
|
|
|
|
- return true;
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error("[Main] 设置 cookies 失败:", error);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-});
|
|
|
|
|
-ipcMain.handle("capture-webview-page", async (_event, webContentsId) => {
|
|
|
|
|
- try {
|
|
|
|
|
- const wc = webContents.fromId(webContentsId);
|
|
|
|
|
- if (!wc) {
|
|
|
|
|
- console.error("找不到 webContents:", webContentsId);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- const image = await wc.capturePage();
|
|
|
|
|
- if (!image || image.isEmpty()) {
|
|
|
|
|
- console.warn("截图为空");
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- const buffer = image.toJPEG(80);
|
|
|
|
|
- return buffer.toString("base64");
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error("截图失败:", error);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
-});
|
|
|
|
|
-ipcMain.handle("webview-send-mouse-click", async (_event, webContentsId, x, y) => {
|
|
|
|
|
- try {
|
|
|
|
|
- const wc = webContents.fromId(webContentsId);
|
|
|
|
|
- if (!wc) {
|
|
|
|
|
- console.error("找不到 webContents:", webContentsId);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- wc.sendInputEvent({
|
|
|
|
|
- type: "mouseMove",
|
|
|
|
|
- x: Math.round(x),
|
|
|
|
|
- y: Math.round(y)
|
|
|
|
|
- });
|
|
|
|
|
- await new Promise((resolve) => setTimeout(resolve, 50));
|
|
|
|
|
- wc.sendInputEvent({
|
|
|
|
|
- type: "mouseDown",
|
|
|
|
|
- x: Math.round(x),
|
|
|
|
|
- y: Math.round(y),
|
|
|
|
|
- button: "left",
|
|
|
|
|
- clickCount: 1
|
|
|
|
|
- });
|
|
|
|
|
- await new Promise((resolve) => setTimeout(resolve, 50));
|
|
|
|
|
- wc.sendInputEvent({
|
|
|
|
|
- type: "mouseUp",
|
|
|
|
|
- x: Math.round(x),
|
|
|
|
|
- y: Math.round(y),
|
|
|
|
|
- button: "left",
|
|
|
|
|
- clickCount: 1
|
|
|
|
|
- });
|
|
|
|
|
- console.log(`[webview-send-mouse-click] Clicked at (${x}, ${y})`);
|
|
|
|
|
- return true;
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error("发送点击事件失败:", error);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-});
|
|
|
|
|
-ipcMain.handle("webview-send-text-input", async (_event, webContentsId, text) => {
|
|
|
|
|
- try {
|
|
|
|
|
- const wc = webContents.fromId(webContentsId);
|
|
|
|
|
- if (!wc) {
|
|
|
|
|
- console.error("找不到 webContents:", webContentsId);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- for (const char of text) {
|
|
|
|
|
- wc.sendInputEvent({
|
|
|
|
|
- type: "char",
|
|
|
|
|
- keyCode: char
|
|
|
|
|
- });
|
|
|
|
|
- await new Promise((resolve) => setTimeout(resolve, 30));
|
|
|
|
|
- }
|
|
|
|
|
- console.log(`[webview-send-text-input] Typed: ${text}`);
|
|
|
|
|
- return true;
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error("发送输入事件失败:", error);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-});
|
|
|
|
|
-ipcMain.handle("webview-get-element-position", async (_event, webContentsId, selector) => {
|
|
|
|
|
- try {
|
|
|
|
|
- const wc = webContents.fromId(webContentsId);
|
|
|
|
|
- if (!wc) {
|
|
|
|
|
- console.error("找不到 webContents:", webContentsId);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
- const result = await wc.executeJavaScript(`
|
|
|
|
|
- (function() {
|
|
|
|
|
- const el = document.querySelector('${selector.replace(/'/g, "\\'")}');
|
|
|
|
|
- if (!el) return null;
|
|
|
|
|
- const rect = el.getBoundingClientRect();
|
|
|
|
|
- return {
|
|
|
|
|
- x: rect.left + rect.width / 2,
|
|
|
|
|
- y: rect.top + rect.height / 2,
|
|
|
|
|
- width: rect.width,
|
|
|
|
|
- height: rect.height
|
|
|
|
|
- };
|
|
|
|
|
- })()
|
|
|
|
|
- `);
|
|
|
|
|
- return result;
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error("获取元素位置失败:", error);
|
|
|
|
|
- return null;
|
|
|
|
|
- }
|
|
|
|
|
-});
|
|
|
|
|
-ipcMain.handle("webview-click-by-text", async (_event, webContentsId, text) => {
|
|
|
|
|
- try {
|
|
|
|
|
- const wc = webContents.fromId(webContentsId);
|
|
|
|
|
- if (!wc) {
|
|
|
|
|
- console.error("找不到 webContents:", webContentsId);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- const position = await wc.executeJavaScript(`
|
|
|
|
|
- (function() {
|
|
|
|
|
- const searchText = '${text.replace(/'/g, "\\'")}';
|
|
|
|
|
-
|
|
|
|
|
- // 查找可点击元素
|
|
|
|
|
- const clickables = document.querySelectorAll('a, button, [role="button"], [onclick], input[type="submit"], input[type="button"]');
|
|
|
|
|
- for (const el of clickables) {
|
|
|
|
|
- if (el.textContent?.includes(searchText) || el.getAttribute('aria-label')?.includes(searchText) || el.getAttribute('title')?.includes(searchText)) {
|
|
|
|
|
- const rect = el.getBoundingClientRect();
|
|
|
|
|
- if (rect.width > 0 && rect.height > 0) {
|
|
|
|
|
- return { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 };
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // 查找所有包含文本的元素
|
|
|
|
|
- const allElements = document.querySelectorAll('*');
|
|
|
|
|
- for (const el of allElements) {
|
|
|
|
|
- const text = el.innerText?.trim();
|
|
|
|
|
- if (text && text.length < 100 && text.includes(searchText)) {
|
|
|
|
|
- const rect = el.getBoundingClientRect();
|
|
|
|
|
- if (rect.width > 0 && rect.height > 0 && rect.width < 500) {
|
|
|
|
|
- return { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 };
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- return null;
|
|
|
|
|
- })()
|
|
|
|
|
- `);
|
|
|
|
|
- if (!position) {
|
|
|
|
|
- console.warn(`[webview-click-by-text] 未找到包含 "${text}" 的元素`);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- wc.sendInputEvent({ type: "mouseMove", x: Math.round(position.x), y: Math.round(position.y) });
|
|
|
|
|
- await new Promise((resolve) => setTimeout(resolve, 50));
|
|
|
|
|
- wc.sendInputEvent({ type: "mouseDown", x: Math.round(position.x), y: Math.round(position.y), button: "left", clickCount: 1 });
|
|
|
|
|
- await new Promise((resolve) => setTimeout(resolve, 50));
|
|
|
|
|
- wc.sendInputEvent({ type: "mouseUp", x: Math.round(position.x), y: Math.round(position.y), button: "left", clickCount: 1 });
|
|
|
|
|
- console.log(`[webview-click-by-text] Clicked "${text}" at (${position.x}, ${position.y})`);
|
|
|
|
|
- return true;
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error("通过文本点击失败:", error);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-});
|
|
|
|
|
-const networkInterceptors = /* @__PURE__ */ new Map();
|
|
|
|
|
-ipcMain.handle("enable-network-intercept", async (_event, webContentsId, patterns) => {
|
|
|
|
|
- var _a;
|
|
|
|
|
- try {
|
|
|
|
|
- const wc = webContents.fromId(webContentsId);
|
|
|
|
|
- if (!wc) {
|
|
|
|
|
- console.error("[CDP] 找不到 webContents:", webContentsId);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
- if (networkInterceptors.has(webContentsId)) {
|
|
|
|
|
- try {
|
|
|
|
|
- wc.debugger.detach();
|
|
|
|
|
- } catch (e) {
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- networkInterceptors.set(webContentsId, {
|
|
|
|
|
- patterns,
|
|
|
|
|
- pendingRequests: /* @__PURE__ */ new Map()
|
|
|
|
|
- });
|
|
|
|
|
- try {
|
|
|
|
|
- wc.debugger.attach("1.3");
|
|
|
|
|
- } catch (err) {
|
|
|
|
|
- const error = err;
|
|
|
|
|
- if (!((_a = error.message) == null ? void 0 : _a.includes("Already attached"))) {
|
|
|
|
|
- throw err;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- await wc.debugger.sendCommand("Network.enable");
|
|
|
|
|
- wc.debugger.on("message", async (_e, method, params) => {
|
|
|
|
|
- const config = networkInterceptors.get(webContentsId);
|
|
|
|
|
- if (!config) return;
|
|
|
|
|
- if (method === "Network.responseReceived") {
|
|
|
|
|
- const { requestId, response } = params;
|
|
|
|
|
- if (!requestId || !(response == null ? void 0 : response.url)) return;
|
|
|
|
|
- if (response.url.includes("baijiahao.baidu.com")) {
|
|
|
|
|
- if (response.url.includes("/pcui/") || response.url.includes("/article")) {
|
|
|
|
|
- console.log(`[CDP DEBUG] 百家号 API: ${response.url}`);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- for (const pattern of config.patterns) {
|
|
|
|
|
- if (response.url.includes(pattern.match)) {
|
|
|
|
|
- config.pendingRequests.set(requestId, {
|
|
|
|
|
- url: response.url,
|
|
|
|
|
- timestamp: Date.now()
|
|
|
|
|
- });
|
|
|
|
|
- console.log(`[CDP] 匹配到 API: ${pattern.key} - ${response.url}`);
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- if (method === "Network.loadingFinished") {
|
|
|
|
|
- const { requestId } = params;
|
|
|
|
|
- if (!requestId) return;
|
|
|
|
|
- const pending = config.pendingRequests.get(requestId);
|
|
|
|
|
- if (!pending) return;
|
|
|
|
|
- config.pendingRequests.delete(requestId);
|
|
|
|
|
- try {
|
|
|
|
|
- const result = await wc.debugger.sendCommand("Network.getResponseBody", { requestId });
|
|
|
|
|
- let body = result.body;
|
|
|
|
|
- if (result.base64Encoded) {
|
|
|
|
|
- body = Buffer.from(body, "base64").toString("utf8");
|
|
|
|
|
- }
|
|
|
|
|
- const data = JSON.parse(body);
|
|
|
|
|
- let matchedKey = "";
|
|
|
|
|
- for (const pattern of config.patterns) {
|
|
|
|
|
- if (pending.url.includes(pattern.match)) {
|
|
|
|
|
- matchedKey = pattern.key;
|
|
|
|
|
- break;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- if (matchedKey) {
|
|
|
|
|
- console.log(`[CDP] 获取到响应: ${matchedKey}`, JSON.stringify(data).substring(0, 200));
|
|
|
|
|
- mainWindow == null ? void 0 : mainWindow.webContents.send("network-intercept-data", {
|
|
|
|
|
- webContentsId,
|
|
|
|
|
- key: matchedKey,
|
|
|
|
|
- url: pending.url,
|
|
|
|
|
- data
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
- } catch (err) {
|
|
|
|
|
- console.warn(`[CDP] 获取响应体失败:`, err);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
- console.log(`[CDP] 已启用网络拦截,webContentsId: ${webContentsId}, patterns:`, patterns.map((p) => p.key));
|
|
|
|
|
- return true;
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error("[CDP] 启用网络拦截失败:", error);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-});
|
|
|
|
|
-ipcMain.handle("disable-network-intercept", async (_event, webContentsId) => {
|
|
|
|
|
- try {
|
|
|
|
|
- const wc = webContents.fromId(webContentsId);
|
|
|
|
|
- if (wc) {
|
|
|
|
|
- try {
|
|
|
|
|
- wc.debugger.detach();
|
|
|
|
|
- } catch (e) {
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
- networkInterceptors.delete(webContentsId);
|
|
|
|
|
- console.log(`[CDP] 已禁用网络拦截,webContentsId: ${webContentsId}`);
|
|
|
|
|
- return true;
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error("[CDP] 禁用网络拦截失败:", error);
|
|
|
|
|
- return false;
|
|
|
|
|
- }
|
|
|
|
|
-});
|
|
|
|
|
-ipcMain.handle("update-network-patterns", async (_event, webContentsId, patterns) => {
|
|
|
|
|
- const config = networkInterceptors.get(webContentsId);
|
|
|
|
|
- if (config) {
|
|
|
|
|
- config.patterns = patterns;
|
|
|
|
|
- console.log(`[CDP] 已更新 patterns,webContentsId: ${webContentsId}`);
|
|
|
|
|
- return true;
|
|
|
|
|
- }
|
|
|
|
|
- return false;
|
|
|
|
|
-});
|
|
|
|
|
-//# sourceMappingURL=main.js.map
|
|
|