mirror of
https://github.com/HugoAura/Seewo-HugoAura.git
synced 2026-06-21 23:54:26 +08:00
1. [/] 修复了 PLS 下载时, 目录未递归创建导致的 ENOENT 错误。 2. [+] 现在可以通过多种方式访问 Aura 设置 UI 了, 更多详细信息, 请参见 #18。 3. [↑] 优化了 Tooltip 的渲染逻辑。
208 lines
5.3 KiB
JavaScript
Executable File
208 lines
5.3 KiB
JavaScript
Executable File
// @ts-check
|
|
|
|
const __SCOPE = "main";
|
|
|
|
const { exec } = require("child_process");
|
|
const nodeHttp = require("http");
|
|
const nodeHttps = require("https");
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
|
|
const { genRandomHex } = require("../../../utils/crypto");
|
|
|
|
const composableFunctions = {
|
|
/**
|
|
*
|
|
* @param {string} url
|
|
* @param {string} targetPath
|
|
* @param {((arg: DownloadTask) => any)} progressCallback
|
|
*/
|
|
downloadFile: async (url, targetPath, progressCallback) => {
|
|
if (!progressCallback) return false;
|
|
const taskId = genRandomHex();
|
|
|
|
/**
|
|
* @type {DownloadTask}
|
|
*/
|
|
const failedTemplate = {
|
|
id: taskId,
|
|
progress: 100,
|
|
status: "failed",
|
|
dlUrl: url,
|
|
savePath: targetPath,
|
|
message: "",
|
|
};
|
|
|
|
if (!url || !targetPath) {
|
|
failedTemplate.message = "Invalid arg";
|
|
progressCallback(failedTemplate);
|
|
return false;
|
|
}
|
|
|
|
const dirName = path.dirname(targetPath);
|
|
|
|
if (!fs.existsSync(dirName)) {
|
|
fs.mkdirSync(dirName, { recursive: true });
|
|
}
|
|
|
|
const httpModuleIns = url.startsWith("https") ? nodeHttps : nodeHttp;
|
|
|
|
global.__HUGO_AURA__.fsTasks?.downloadTasks.set(taskId, {
|
|
status: "waiting",
|
|
cancelReq: null,
|
|
});
|
|
|
|
progressCallback({
|
|
id: taskId,
|
|
progress: 0,
|
|
status: "waiting",
|
|
dlUrl: url,
|
|
savePath: targetPath,
|
|
});
|
|
|
|
const fsStream = fs.createWriteStream(targetPath);
|
|
|
|
const dlReq = httpModuleIns.get(url, (response) => {
|
|
if (response.statusCode !== 200) {
|
|
fsStream.close();
|
|
failedTemplate.message = `Request error: HTTP ${response.statusCode}`;
|
|
progressCallback(failedTemplate);
|
|
return false;
|
|
}
|
|
|
|
const contentLength = response.headers["content-length"];
|
|
// @ts-expect-error
|
|
const totalBytes = parseInt(contentLength, 10) || 0; // No error handling 😆
|
|
let curRecvBytes = 0;
|
|
|
|
let hasCancelled = false;
|
|
|
|
global.__HUGO_AURA__.fsTasks?.downloadTasks.set(taskId, {
|
|
status: "progressing",
|
|
cancelReq: () => {
|
|
hasCancelled = true;
|
|
dlReq.destroy();
|
|
fsStream.close();
|
|
fs.unlink(targetPath, () => {});
|
|
global.__HUGO_AURA__.fsTasks?.downloadTasks.delete(taskId);
|
|
progressCallback({
|
|
id: taskId,
|
|
progress: 100,
|
|
curBytes: curRecvBytes,
|
|
totalBytes: totalBytes,
|
|
status: "cancelled",
|
|
dlUrl: url,
|
|
savePath: targetPath,
|
|
});
|
|
},
|
|
});
|
|
|
|
response.on("data", (chunk) => {
|
|
curRecvBytes += chunk.length;
|
|
const curProgress =
|
|
totalBytes > 0 ? (curRecvBytes / totalBytes) * 100 : 0;
|
|
progressCallback({
|
|
id: taskId,
|
|
progress: curProgress.toFixed(2),
|
|
curBytes: curRecvBytes,
|
|
totalBytes: totalBytes,
|
|
status: "progressing",
|
|
dlUrl: url,
|
|
savePath: targetPath,
|
|
});
|
|
});
|
|
|
|
response.pipe(fsStream);
|
|
|
|
fsStream.on("finish", () => {
|
|
fsStream.close();
|
|
if (hasCancelled) {
|
|
return;
|
|
}
|
|
progressCallback({
|
|
id: taskId,
|
|
progress: (100).toFixed(2),
|
|
curBytes: curRecvBytes,
|
|
totalBytes: totalBytes,
|
|
status: "done",
|
|
dlUrl: url,
|
|
savePath: targetPath,
|
|
});
|
|
global.__HUGO_AURA__.fsTasks?.downloadTasks.delete(taskId);
|
|
});
|
|
|
|
return true;
|
|
});
|
|
|
|
dlReq.on("error", (e) => {
|
|
fsStream.close();
|
|
fs.unlink(targetPath, () => {});
|
|
failedTemplate.message =
|
|
"Request error: Unexpected error while downloading file";
|
|
failedTemplate.errorObj = e;
|
|
console.error(
|
|
`[HugoAura / IPC / FS / ERROR] Error downloading file from ${url}, errorObj:`,
|
|
e
|
|
);
|
|
progressCallback(failedTemplate);
|
|
global.__HUGO_AURA__.fsTasks?.downloadTasks.delete(taskId);
|
|
return false;
|
|
});
|
|
},
|
|
};
|
|
|
|
/**
|
|
*
|
|
* @param {import("electron").IpcMain} ipcMain
|
|
*/
|
|
const applyFsIpcHandler = (ipcMain) => {
|
|
const methodBase = "$aura.fs";
|
|
|
|
global.__HUGO_AURA__.fsTasks = {
|
|
downloadTasks: new Map(),
|
|
};
|
|
|
|
ipcMain.handle(
|
|
`${methodBase}.dl.cancelDownloadTask`,
|
|
/**
|
|
*
|
|
* @param {import("electron").IpcMainInvokeEvent} _evt
|
|
* @param {{ targetTaskId: string }} arg
|
|
* @returns {{ success: boolean, error: string | null }}
|
|
*/
|
|
(_evt, arg) => {
|
|
if (!arg.targetTaskId) {
|
|
return {
|
|
success: false,
|
|
error: "ARG_INVALID",
|
|
};
|
|
}
|
|
|
|
if (!global.__HUGO_AURA__.fsTasks?.downloadTasks.has(arg.targetTaskId)) {
|
|
return {
|
|
success: false,
|
|
error: "TASK_ID_NOT_FOUND",
|
|
};
|
|
}
|
|
|
|
const taskObj = global.__HUGO_AURA__.fsTasks.downloadTasks.get(
|
|
arg.targetTaskId
|
|
);
|
|
if (!taskObj?.cancelReq) {
|
|
return {
|
|
success: false,
|
|
error: "TASK_NOT_STARTED",
|
|
};
|
|
}
|
|
|
|
taskObj.cancelReq();
|
|
return {
|
|
success: true,
|
|
error: null,
|
|
};
|
|
}
|
|
);
|
|
};
|
|
|
|
module.exports = { fsComposables: composableFunctions, applyFsIpcHandler };
|