mirror of
https://github.com/fanchenio/DawnLauncher.git
synced 2025-07-14 05:12:11 +08:00
584 lines
15 KiB
TypeScript
584 lines
15 KiB
TypeScript
import { Result } from "../../../types/common";
|
|
import { resolve, dirname, relative, join } from "node:path";
|
|
import { mkdirSync, existsSync } from "node:fs";
|
|
import mime from "mime";
|
|
import retry from "retry";
|
|
import request from "request";
|
|
import * as cheerio from "cheerio";
|
|
import { isAbsolutePath } from "../../../commons/utils/common";
|
|
import {
|
|
BrowserWindow,
|
|
Display,
|
|
OpenDialogOptions,
|
|
SaveDialogOptions,
|
|
app,
|
|
dialog,
|
|
nativeImage,
|
|
nativeTheme,
|
|
screen,
|
|
} from "electron";
|
|
import { getRandomUserAgent, iconExts } from "../../commons/utils";
|
|
import URI from "urijs";
|
|
import { hideMainWindow } from "../main";
|
|
import { hideQuickSearchWindow } from "../search";
|
|
|
|
/**
|
|
* 获取代理
|
|
*/
|
|
function getProxy() {
|
|
if (
|
|
global.setting.network.useProxy &&
|
|
global.setting.network.proxy.address &&
|
|
global.setting.network.proxy.address.trim() !== ""
|
|
) {
|
|
let uri = new URI(global.setting.network.proxy.address);
|
|
if (uri.protocol() && uri.protocol().trim() !== "") {
|
|
let address = uri.protocol().toLowerCase() + "://";
|
|
if (
|
|
global.setting.network.proxy.username &&
|
|
global.setting.network.proxy.username.trim() !== "" &&
|
|
global.setting.network.proxy.password &&
|
|
global.setting.network.proxy.password.trim() !== ""
|
|
) {
|
|
address +=
|
|
global.setting.network.proxy.username +
|
|
":" +
|
|
global.setting.network.proxy.password +
|
|
"@";
|
|
}
|
|
address += uri.hostname() + ":" + uri.port();
|
|
return address;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* 下载图片
|
|
* @param windowName
|
|
* @param url
|
|
*/
|
|
function downloadImage(windowName: string, url: string) {
|
|
let result: Result = {
|
|
status: false,
|
|
message: global.language.downloadImagePrompt1,
|
|
icon: null,
|
|
name: null,
|
|
};
|
|
// 重试
|
|
const operation = retry.operation({
|
|
retries: 5, // 最多重试 5 次
|
|
factor: 1, // 每次重试之间的时间间隔加倍
|
|
minTimeout: 1000, // 第一次重试之前等待的时间
|
|
maxTimeout: 5000, // 最长等待时间
|
|
});
|
|
operation.attempt((currentAttempt) => {
|
|
// 下载图片
|
|
request(
|
|
{
|
|
uri: url,
|
|
proxy: getProxy(),
|
|
encoding: null,
|
|
timeout: 5000,
|
|
headers: {
|
|
"User-Agent": getRandomUserAgent(),
|
|
},
|
|
maxRedirects: 5,
|
|
jar: true,
|
|
},
|
|
function (error, response, body) {
|
|
if (operation.retry(error)) {
|
|
return;
|
|
}
|
|
if (
|
|
!error &&
|
|
response.statusCode >= 200 &&
|
|
response.statusCode <= 299
|
|
) {
|
|
if (response.headers && response.headers["content-type"]) {
|
|
let ext = mime.getExtension(response.headers["content-type"]);
|
|
if (iconExts.includes(ext)) {
|
|
let buffer = Buffer.from(body);
|
|
result.icon =
|
|
"data:" +
|
|
mime.getType(response.headers["content-type"]) +
|
|
";base64," +
|
|
buffer.toString("base64");
|
|
result.status = true;
|
|
result.message = null;
|
|
} else {
|
|
result.icon = null;
|
|
result.status = false;
|
|
result.message = global.language.downloadImagePrompt2;
|
|
}
|
|
}
|
|
}
|
|
// window
|
|
sendToWebContent(windowName, "onDownloadImage", result);
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 获取网址信息
|
|
* @param windowName
|
|
* @param url
|
|
* @param redirect
|
|
*/
|
|
function getURLInfo(windowName: string, url: string, redirect: boolean) {
|
|
let result: Result = {
|
|
status: false,
|
|
message: null,
|
|
name: null,
|
|
icon: null,
|
|
};
|
|
// 重试
|
|
const operation = retry.operation({
|
|
retries: 5, // 最多重试 5 次
|
|
factor: 1, // 每次重试之间的时间间隔加倍
|
|
minTimeout: 1000, // 第一次重试之前等待的时间
|
|
maxTimeout: 5000, // 最长等待时间
|
|
});
|
|
try {
|
|
// 发起请求
|
|
operation.attempt((currentAttempt) => {
|
|
request(
|
|
{
|
|
uri: url,
|
|
proxy: getProxy(),
|
|
timeout: 5000,
|
|
headers: {
|
|
"User-Agent": getRandomUserAgent(),
|
|
},
|
|
maxRedirects: 5,
|
|
jar: true,
|
|
},
|
|
function (error, response, body) {
|
|
if (operation.retry(error)) {
|
|
return;
|
|
}
|
|
if (
|
|
!error &&
|
|
response.statusCode >= 200 &&
|
|
response.statusCode <= 299
|
|
) {
|
|
const $ = cheerio.load(body);
|
|
// 是否有跳转标签
|
|
let refresh = $("meta[http-equiv='refresh']");
|
|
// content
|
|
let content = refresh.attr("content");
|
|
if (content && content.trim() !== "" && redirect) {
|
|
// 如果有跳转标签的话,就请求新网址并获取网址信息
|
|
let contentSplit = content.split(";");
|
|
let urlProperty = contentSplit[contentSplit.length - 1];
|
|
let urlPropertySplit = urlProperty.split("=");
|
|
let newURL = urlPropertySplit[urlPropertySplit.length - 1];
|
|
// 重新获取新网址信息
|
|
getURLInfo(windowName, newURL, false);
|
|
} else {
|
|
// 解析HTML并返回信息
|
|
analysisHTML(windowName, url, body);
|
|
}
|
|
} else {
|
|
sendUrlInfo(windowName, result);
|
|
}
|
|
}
|
|
);
|
|
});
|
|
} catch (e) {
|
|
sendUrlInfo(windowName, result);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 解析HTML并返回信息
|
|
* @param windowName
|
|
* @param url
|
|
* @param data
|
|
*/
|
|
function analysisHTML(windowName: string, url: string, data: string) {
|
|
let result: Result = {
|
|
status: false,
|
|
message: null,
|
|
name: null,
|
|
icon: null,
|
|
};
|
|
try {
|
|
// 解析HTML
|
|
let $ = cheerio.load(data);
|
|
// 获取标题
|
|
result.name = $("head").find("title").text();
|
|
if (!result.name || result.name.trim() === "") {
|
|
result.name = $("title").text();
|
|
}
|
|
// 获取图标URL
|
|
let iconURL: string | null = null;
|
|
let icon = $("link[rel='icon']");
|
|
let href = icon.attr("href");
|
|
if (href && href.trim() !== "") {
|
|
iconURL = href;
|
|
} else {
|
|
let shortcutIcon = $("link[rel='shortcut icon']");
|
|
let shortcutIconhref = shortcutIcon.attr("href");
|
|
if (shortcutIconhref && shortcutIconhref.trim() !== "") {
|
|
iconURL = shortcutIconhref;
|
|
} else {
|
|
iconURL = "/favicon.ico";
|
|
}
|
|
}
|
|
if (iconURL) {
|
|
// 去掉类似//www.baidu.com/favicon.ico这样域名的”//“字符
|
|
if (iconURL.indexOf("//") === 0) {
|
|
iconURL = "http:" + iconURL;
|
|
}
|
|
// 无协议头,使用当前网址域名
|
|
if (iconURL.indexOf("http://") < 0 && iconURL.indexOf("https://") < 0) {
|
|
iconURL = url + (iconURL.indexOf("//") === 0 ? "" : "//") + iconURL;
|
|
}
|
|
// 重试
|
|
const operation = retry.operation({
|
|
retries: 5, // 最多重试 5 次
|
|
factor: 1, // 每次重试之间的时间间隔加倍
|
|
minTimeout: 1000, // 第一次重试之前等待的时间
|
|
maxTimeout: 5000, // 最长等待时间
|
|
});
|
|
operation.attempt((currentAttempt) => {
|
|
// 下载图标
|
|
request(
|
|
{
|
|
uri: iconURL,
|
|
proxy: getProxy(),
|
|
encoding: null,
|
|
timeout: 5000,
|
|
headers: {
|
|
"User-Agent": getRandomUserAgent(),
|
|
},
|
|
maxRedirects: 5,
|
|
jar: true,
|
|
},
|
|
function (error, response, body) {
|
|
if (operation.retry(error)) {
|
|
return;
|
|
}
|
|
if (
|
|
!error &&
|
|
response.statusCode >= 200 &&
|
|
response.statusCode <= 299
|
|
) {
|
|
let buffer = Buffer.from(body);
|
|
result.icon =
|
|
"data:" +
|
|
mime.getType(iconURL) +
|
|
";base64," +
|
|
buffer.toString("base64");
|
|
result.status = true;
|
|
sendUrlInfo(windowName, result);
|
|
} else {
|
|
if (result.name && result.name.trim() !== "") {
|
|
result.status = true;
|
|
}
|
|
sendUrlInfo(windowName, result);
|
|
}
|
|
}
|
|
);
|
|
});
|
|
} else {
|
|
sendUrlInfo(windowName, result);
|
|
}
|
|
} catch (e) {
|
|
sendUrlInfo(windowName, result);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 发送网址信息
|
|
* @param result
|
|
*/
|
|
function sendUrlInfo(windowName: string, result: Result) {
|
|
sendToWebContent(windowName, "onGetURLInfo", result);
|
|
}
|
|
|
|
/**
|
|
* 路径转换
|
|
* @param path
|
|
*/
|
|
function convertPath(path: string) {
|
|
let appPath =
|
|
process.env.NODE_ENV === "development"
|
|
? resolve(".")
|
|
: dirname(process.execPath);
|
|
if (isAbsolutePath(path)) {
|
|
return relative(appPath, path);
|
|
} else {
|
|
return resolve(appPath, path);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 发送IPC到所有窗口
|
|
* @param channel
|
|
* @param data
|
|
*/
|
|
function sendAllWindows(channel: string, data: any) {
|
|
for (const window of BrowserWindow.getAllWindows()) {
|
|
if (!window.isDestroyed()) {
|
|
window.webContents.send(channel, data);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* 获取窗口实例
|
|
* @param name
|
|
*/
|
|
function getWindow(name: string): BrowserWindow | null {
|
|
let window: BrowserWindow | null = null;
|
|
if (name === "mainWindow") {
|
|
window = global.mainWindow;
|
|
} else if (name === "quickSearchWindow") {
|
|
window = global.quickSearchWindow;
|
|
} else if (name === "settingWindow") {
|
|
window = global.settingWindow;
|
|
} else if (name === "classificationAddEditWindow") {
|
|
window = global.classificationAddEditWindow;
|
|
} else if (name === "classificationSetIconWindow") {
|
|
window = global.classificationSetIconWindow;
|
|
} else if (name === "classificationAssociateFolderWindow") {
|
|
window = global.classificationAssociateFolderWindow;
|
|
} else if (name === "classificationAggregateWindow") {
|
|
window = global.classificationAggregateWindow;
|
|
} else if (name === "itemAddEditWindow") {
|
|
window = global.itemAddEditWindow;
|
|
} else if (name === "itemNetworkIconWindow") {
|
|
window = global.itemNetworkIconWindow;
|
|
} else if (name === "itemSVGIconWindow") {
|
|
window = global.itemSVGIconWindow;
|
|
} else if (name === "aboutWindow") {
|
|
window = global.aboutWindow;
|
|
} else if (name === "backupRestoreDataWindow") {
|
|
window = global.backupRestoreDataWindow;
|
|
}
|
|
if (window && !window.isDestroyed()) {
|
|
return window;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 关闭窗口
|
|
* @param window
|
|
*/
|
|
function closeWindow(window: BrowserWindow | null) {
|
|
if (window && !window.isDestroyed() && window.isVisible()) {
|
|
window.close();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取图标点
|
|
*/
|
|
function getDot() {
|
|
return nativeImage.createFromDataURL(
|
|
!nativeTheme.shouldUseDarkColors
|
|
? "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAERJREFUOE9jZKAQMFKon2HUAAa8YWDDwMBQCQ3kdgYGhiPYAhxfIG5lYGDwgmraxsDA4E13Ayj2AlFpbDQh4U9IRAUiAEXYCBFBtkaAAAAAAElFTkSuQmCC"
|
|
: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAEtJREFUOE9jZKAQMFKon2HUAAbcYfD//38bBgaGSmggtzMyMh7BFuA4A/H///9bGRgYvKCatjEyMnrT3QDKvEBsAhtNSHgSErGBCAD0gBQREV/HsgAAAABJRU5ErkJggg=="
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 发送消息到页面
|
|
* @param windowName
|
|
* @param listener
|
|
* @param params
|
|
*/
|
|
function sendToWebContent(windowName: string, listener: string, params: any) {
|
|
// 获取窗口
|
|
let window = getWindow(windowName);
|
|
if (window && !window.isDestroyed()) {
|
|
window.webContents.send(listener, params);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 关闭所有子进程
|
|
*/
|
|
function closeAllChildProcess() {
|
|
if (global.childProcessMap) {
|
|
global.childProcessMap.forEach((value, key) => {
|
|
try {
|
|
value.utilityProcess.kill();
|
|
} catch (e) {}
|
|
try {
|
|
value.port1.close();
|
|
} catch (e) {}
|
|
try {
|
|
value.port2.close();
|
|
} catch (e) {}
|
|
});
|
|
global.childProcessMap.clear();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 打开后隐藏窗口
|
|
* @param type
|
|
*/
|
|
function openAfterHideWindow(type: string) {
|
|
if (type === "main" || type === "search") {
|
|
if (global.setting.item.openAfterHideMainInterface) {
|
|
hideMainWindow();
|
|
}
|
|
} else if (type === "quickSearch") {
|
|
if (global.setting.quickSearch.openAfterHideQuickSearchWindow) {
|
|
hideQuickSearchWindow();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 重启
|
|
*/
|
|
function relaunch() {
|
|
app.relaunch();
|
|
app.quit();
|
|
}
|
|
|
|
// 数据存储目录
|
|
function getUserDataPath() {
|
|
let userDataPath = app.getPath("userData");
|
|
if (
|
|
process.env.NODE_ENV !== "development" &&
|
|
import.meta.env.VITE_INSTALL === "false"
|
|
) {
|
|
userDataPath = join(dirname(process.execPath), "data");
|
|
if (!existsSync(userDataPath)) {
|
|
mkdirSync(userDataPath, { recursive: true });
|
|
}
|
|
}
|
|
return userDataPath;
|
|
}
|
|
|
|
/**
|
|
* 获取主背景颜色
|
|
*/
|
|
function getMainBackgorunColor() {
|
|
let backgroundColor = global.setting.appearance.theme.mainBackgroundColor;
|
|
if (backgroundColor.length === 9) {
|
|
return backgroundColor.substring(0, 7);
|
|
} else {
|
|
return backgroundColor;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取窗口所在的屏幕
|
|
*/
|
|
function getWindowInScreen(window: BrowserWindow) {
|
|
let inDisplays: Array<Display> = [];
|
|
let displays = screen.getAllDisplays();
|
|
let bounds = window.getBounds();
|
|
for (let display of displays) {
|
|
let workArea = display.workArea;
|
|
if (
|
|
((workArea.x <= bounds.x && workArea.x + workArea.width >= bounds.x) ||
|
|
(workArea.x <= bounds.x + bounds.width &&
|
|
workArea.x + workArea.width >= bounds.x + bounds.width)) &&
|
|
((workArea.y <= bounds.y && workArea.y + workArea.height >= bounds.y) ||
|
|
(workArea.y <= bounds.y + bounds.height &&
|
|
workArea.y + workArea.height >= bounds.y + bounds.height))
|
|
) {
|
|
inDisplays.push(display);
|
|
}
|
|
}
|
|
return inDisplays;
|
|
}
|
|
|
|
/**
|
|
* 通用对话框
|
|
*/
|
|
function showMessageBoxSync(
|
|
windowName: string,
|
|
message: string,
|
|
type: "error" | "question" | "info",
|
|
buttons: Array<string> | null
|
|
) {
|
|
// 记录状态
|
|
if (windowName === "mainWindow") {
|
|
global.mainWindowShowDialog = true;
|
|
}
|
|
let res = dialog.showMessageBoxSync(getWindow(windowName), {
|
|
title: "Dawn Launcher",
|
|
message: message,
|
|
buttons: buttons,
|
|
type: type,
|
|
noLink: true,
|
|
cancelId: 1,
|
|
});
|
|
// 删除状态
|
|
if (windowName === "mainWindow") {
|
|
global.mainWindowShowDialog = false;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* 错误对话框
|
|
*/
|
|
function showErrorMessageBox(windowName: string, message: string) {
|
|
showMessageBoxSync(windowName, message, "error", [global.language.ok]);
|
|
}
|
|
|
|
/**
|
|
* 文件保存对话框
|
|
*/
|
|
function showSaveDialogSync(windowName: string, options: SaveDialogOptions) {
|
|
// 记录状态
|
|
if (windowName === "mainWindow") {
|
|
global.mainWindowShowDialog = true;
|
|
}
|
|
let path = dialog.showSaveDialogSync(getWindow(windowName), options);
|
|
// 删除状态
|
|
if (windowName === "mainWindow") {
|
|
global.mainWindowShowDialog = false;
|
|
}
|
|
return path;
|
|
}
|
|
|
|
/**
|
|
* 选择文件/文件夹对话框
|
|
*/
|
|
function showOpenDialogSync(windowName: string, options: OpenDialogOptions) {
|
|
// 记录状态
|
|
if (windowName === "mainWindow") {
|
|
global.mainWindowShowDialog = true;
|
|
}
|
|
let pathList = dialog.showOpenDialogSync(getWindow(windowName), options);
|
|
// 删除状态
|
|
if (windowName === "mainWindow") {
|
|
global.mainWindowShowDialog = false;
|
|
}
|
|
return pathList;
|
|
}
|
|
|
|
export {
|
|
downloadImage,
|
|
getURLInfo,
|
|
convertPath,
|
|
sendAllWindows,
|
|
closeWindow,
|
|
getDot,
|
|
getWindow,
|
|
sendToWebContent,
|
|
closeAllChildProcess,
|
|
openAfterHideWindow,
|
|
relaunch,
|
|
getUserDataPath,
|
|
getMainBackgorunColor,
|
|
getWindowInScreen,
|
|
showMessageBoxSync,
|
|
showErrorMessageBox,
|
|
showSaveDialogSync,
|
|
showOpenDialogSync,
|
|
};
|