feat: 📦 使用窗口管理

pull/6/head
caorushizi 4 years ago
parent d0e7111fcd
commit 5becbbfc87

@ -0,0 +1,5 @@
import { EventEmitter } from "events";
const eventEmitter = new EventEmitter();
export default eventEmitter;

@ -4,65 +4,26 @@ import path from "path";
import store from "./store";
import { exec, failFn, successFn } from "./utils";
import logger from "./logger";
import XhrFilter from "./xhrFilter";
import windowManager from "./window/windowManager";
import { WindowName } from "./window/variables";
// eslint-disable-next-line global-require
if (require("electron-squirrel-startup")) {
app.quit();
}
const xhrFilter = new XhrFilter();
if (!is.development) {
global.__bin__ = path
.resolve(app.getAppPath(), "../.bin")
.replace(/\\/g, "\\\\");
}
const createMainWindow = async () => {
const mainWindow = new BrowserWindow({
width: 590,
minWidth: 590,
height: 600,
frame: false,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true,
},
});
await mainWindow.loadURL("http://localhost:3000/main_window/");
if (is.development) mainWindow.webContents.openDevTools();
return mainWindow;
};
const createBrowserWindow = async () => {
const browserWindow = new BrowserWindow({
width: 800,
height: 600,
show: false,
frame: false,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true,
},
});
await browserWindow.loadURL("http://localhost:3000/browser_window/");
if (is.development) browserWindow.webContents.openDevTools();
ipcMain.on("openBrowserWindow", () => {
browserWindow.show();
});
ipcMain.on("closeBrowserWindow", () => {
logger.info("closeBrowserWindow");
browserWindow.hide();
});
return browserWindow;
};
const init = async () => {
const mainWindow = await createMainWindow();
const browserWindow = await createBrowserWindow();
const mainWindow = await windowManager.create(WindowName.MAIN_WINDOW);
const browserWindow = await windowManager.create(WindowName.BROWSER_WINDOW);
const partition = "persist:webview";
const ses = session.fromPartition(partition);
@ -78,7 +39,7 @@ const init = async () => {
view.setBounds({ x: 0, y: 0, height: 0, width: 0 });
const { webContents } = view;
// if (is.development) webContents.openDevTools();
if (is.development) webContents.openDevTools();
webContents.on("dom-ready", () => {
webContents.on("new-window", async (event, url) => {
@ -87,28 +48,8 @@ const init = async () => {
});
});
ses.webRequest.onBeforeSendHeaders(
{ urls: ["*://*/*"] },
(details, callback) => {
const m3u8Reg = /\.m3u8$/;
const tsReg = /\.ts$/;
let cancel = false;
const myURL = new URL(details.url);
if (m3u8Reg.test(myURL.pathname)) {
logger.info("在窗口中捕获 m3u8 链接: ", details.url);
mainWindow.webContents.send("m3u8", {
title: webContents.getTitle(),
requestDetails: details,
});
} else if (tsReg.test(myURL.pathname)) {
cancel = true;
}
callback({
cancel,
requestHeaders: details.requestHeaders,
});
}
);
const filter = { urls: ["*://*/*"] };
ses.webRequest.onBeforeSendHeaders(filter, xhrFilter.beforeSendHeaders);
};
app.on("window-all-closed", () => {

@ -0,0 +1,4 @@
export enum WindowName {
MAIN_WINDOW = "MAIN_WINDOW",
BROWSER_WINDOW = "BROWSER_WINDOW",
}

@ -0,0 +1,62 @@
import { IWindowListItem } from "../../types/main";
import { is } from "electron-util";
import { ipcMain } from "electron";
import logger from "../logger";
import { WindowName } from "./variables";
const windowList = new Map<WindowName, IWindowListItem>();
windowList.set(WindowName.MAIN_WINDOW, {
url: is.development
? "http://localhost:3000/main_window/"
: "test://123132123123123",
options() {
return {
width: 590,
minWidth: 590,
height: 600,
frame: false,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true,
},
};
},
async callback(window) {
if (is.development) window.webContents.openDevTools();
},
});
windowList.set(WindowName.BROWSER_WINDOW, {
url: is.development
? "http://localhost:3000/browser_window/"
: "test://123132123123123",
options() {
return {
width: 800,
height: 600,
show: false,
frame: false,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true,
},
};
},
async callback(window) {
if (is.development) window.webContents.openDevTools();
ipcMain.on("openBrowserWindow", () => {
window.show();
});
ipcMain.on("closeBrowserWindow", () => {
logger.info("closeBrowserWindow");
window.hide();
});
},
});
export default windowList;

@ -0,0 +1,41 @@
import { BrowserWindow } from "electron";
import windowList from "./windowList";
import { IWindowListItem, IWindowManager } from "../../types/main";
import { WindowName } from "./variables";
class WindowManager implements IWindowManager {
private windowMap: Map<WindowName | string, BrowserWindow> = new Map();
private windowIdMap: Map<number, WindowName | string> = new Map();
async create(name: WindowName) {
const windowConfig: IWindowListItem = windowList.get(name)!;
const window = new BrowserWindow(windowConfig.options());
const id = window.id;
this.windowMap.set(name, window);
this.windowIdMap.set(window.id, name);
await window.loadURL(windowConfig.url);
await windowConfig.callback(window, this);
window.on("close", () => {
this.deleteById(id);
});
return window;
}
get(name: WindowName) {
return this.windowMap.get(name)!;
}
has(name: WindowName) {
return this.windowMap.has(name);
}
deleteById = (id: number) => {
const name = this.windowIdMap.get(id);
if (name) {
this.windowMap.delete(name);
this.windowIdMap.delete(id);
}
};
}
export default new WindowManager();

@ -0,0 +1,54 @@
import logger from "./logger";
import windowManager from "./window/windowManager";
import { WindowName } from "./window/variables";
class XhrFilter {
// 当请求即将发生时
beforeRequest() {}
// 一旦请求头可用,在发送 HTTP 请求之前listener 将以
// listener(details, callback) 的形式被调用。
// 这可能发生在对服务器进行 TCP 连接之后
// 但在发送任何HTTP数据之前。
beforeSendHeaders(
details: Electron.OnBeforeSendHeadersListenerDetails,
callback: (beforeSendResponse: Electron.BeforeSendResponse) => void
) {
const m3u8Reg = /\.m3u8$/;
const tsReg = /\.ts$/;
let cancel = false;
const myURL = new URL(details.url);
if (m3u8Reg.test(myURL.pathname)) {
logger.info("在窗口中捕获 m3u8 链接: ", details.url);
const { webContents } = windowManager.get(WindowName.MAIN_WINDOW) ?? {};
webContents.send("m3u8", {
title: webContents.getTitle(),
requestDetails: details,
});
} else if (tsReg.test(myURL.pathname)) {
cancel = true;
}
callback({
cancel,
requestHeaders: details.requestHeaders,
});
}
// 在请求发送到服务器之前listener将以listener(details)的形式被调用
// ,在该侦听器被出发前,上一个对 onBeforeSendHeaders
// 响应的修改是可见的。
sendHeaders() {}
// 当HTTP请求接收到报头后会通过调用 listener(details, callback)
// 方法来触发listener。
// The callback has to be called with a response object.
headersReceived() {}
// 当收到响应体的第一个字节时, 将以 listener(details) 的形式来调用 listener。
// 对于 HTTP 请求而言,这意味着此时 HTTP 状态行和回应头已经可以读取了。
responseStarted() {}
// 当服务器的初始重定向即将发生时,将以 listener(details)的方式调用listener。
beforeRedirect() {}
// 当请求完成时,将以 listener(details)的方式调用listener。
completed() {}
// 当发生错误时,将以 listener(details)的方式调用listener。
errorOccurred() {}
}
export default XhrFilter;

@ -42,6 +42,7 @@
"eslint-plugin-react": "7.21.5",
"eslint-plugin-react-hooks": "1.7.0",
"glob": "^7.1.6",
"mime": "^2.5.2",
"prettier": "2.2.1",
"sass": "^1.32.8",
"semver": "^7.3.4",

22
types/main.d.ts vendored

@ -1,3 +1,5 @@
import { BrowserWindow } from "electron";
declare global {
namespace NodeJS {
interface Global {
@ -8,4 +10,22 @@ declare global {
declare var __bin__: string;
export {};
declare enum WindowName {
MAIN_WINDOW = "MAIN_WINDOW",
BROWSER_WINDOW = "BROWSER_WINDOW",
}
declare interface IWindowManager {
create: (name: WindowName) => Promise<BrowserWindow | null>;
get: (name: WindowName) => BrowserWindow | null;
has: (name: WindowName) => boolean;
deleteById: (id: number) => void;
}
declare interface IWindowListItem {
url: string;
options: () => Electron.BrowserWindowConstructorOptions;
callback: (window: BrowserWindow, windowManager: IWindowManager) => void;
}
export { IWindowManager, IWindowListItem };

Loading…
Cancel
Save