[🚧 Fix] <PLS & FS> Improve PLS download logic & UX

1. [+] 增加了 PLS 下载操作的取消功能
2. [/] 修复了 FS IPC 中 `downloadFile` 时, 过早地从 downloadTasks 中删除任务的逻辑错误。
3. [↑] 改进了 PLS IPC 中 `handlePLSDownload` 获取版本信息时的逻辑, 现在该函数会从全局 API 信息中逐个尝试 API 域名。减少了极端网络环境下, 版本信息获取失败的可能性。
4. [/] 修复了下载失败后, 下载按钮依然保持灰显的问题。
5. [+] 为 PLS 下载增加了进度条显示。
6. [/] 优化了 `plsConnectionManager` 中一些不必要的 IPC 状态同步 (有些时候还会导致逻辑错误)。
This commit is contained in:
Minoricew
2025-06-13 11:49:22 +08:00
parent a9d3772b51
commit ca5d94ebd8
10 changed files with 444 additions and 84 deletions

View File

@@ -38,10 +38,13 @@ const composableFunctions = {
progressCallback(failedTemplate);
return false;
}
if (!fs.existsSync(path.dirname(targetPath))) {
failedTemplate.message = "Path not exists";
progressCallback(failedTemplate);
const dirName = path.dirname(targetPath);
if (!fs.existsSync(dirName)) {
fs.mkdirSync(dirName);
}
const httpModuleIns = url.startsWith("https") ? nodeHttps : nodeHttp;
global.__HUGO_AURA__.fsTasks?.downloadTasks.set(taskId, {
@@ -49,6 +52,14 @@ const composableFunctions = {
cancelReq: null,
});
progressCallback({
id: taskId,
progress: 0,
status: "waiting",
dlUrl: url,
savePath: targetPath,
});
const fsStream = fs.createWriteStream(targetPath);
const dlReq = httpModuleIns.get(url, (response) => {
@@ -64,9 +75,12 @@ const composableFunctions = {
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, () => {});
@@ -102,6 +116,9 @@ const composableFunctions = {
fsStream.on("finish", () => {
fsStream.close();
if (hasCancelled) {
return;
}
progressCallback({
id: taskId,
progress: (100).toFixed(2),
@@ -111,9 +128,9 @@ const composableFunctions = {
dlUrl: url,
savePath: targetPath,
});
global.__HUGO_AURA__.fsTasks?.downloadTasks.delete(taskId);
});
global.__HUGO_AURA__.fsTasks?.downloadTasks.delete(taskId);
return true;
});
@@ -123,7 +140,10 @@ const composableFunctions = {
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);
console.error(
`[HugoAura / IPC / FS / ERROR] Error downloading file from ${url}, errorObj:`,
e
);
progressCallback(failedTemplate);
global.__HUGO_AURA__.fsTasks?.downloadTasks.delete(taskId);
return false;

View File

@@ -71,34 +71,92 @@ const functions = {
handlePLSDownload: async (channel, callbackFn, binPath) => {
// TODO: Channel selection
const apiInfo = global.__HUGO_AURA_API__;
let plsVersionInfo = {};
const getVerPromise = new Promise((resolve) => {
const getVerPromise = new Promise(async (resolveGetVerReq) => {
// ↓ 目前 channel param 没有什么用处
nodeHttps
.get(
`${apiInfo.baseUrl}${apiInfo.plsUpdate}?channel=${channel}`,
(rep) => {
let dataChunk = "";
rep.on("data", (chunk) => {
dataChunk += chunk;
});
for (const apiDomain of apiInfo.domains) {
const reqPromise = new Promise((resolveHttpRequest) => {
nodeHttps
.get(
`${apiDomain}${apiInfo.plsUpdate}?channel=${channel}`,
(rep) => {
let dataChunk = "";
rep.on("data", (chunk) => {
dataChunk += chunk;
});
rep.on("end", () => {
resolve({
success: true,
data: dataChunk,
rep.on("end", () => {
let parsedData = {};
try {
parsedData = JSON.parse(dataChunk);
} catch (e) {
callbackFn({
id: "",
progress: 0,
status: "struggling",
dlUrl: null,
savePath: null,
message: `数据解析失败, 正在尝试 API 域名 ${
apiInfo.domains[apiInfo.domains.indexOf(apiDomain) + 1]
} ...`,
errorObj: e,
});
setTimeout(() => {
resolveHttpRequest({
success: false,
errorObj: e,
});
}, 1000);
return;
}
resolveHttpRequest({
success: true,
data: parsedData,
});
return;
});
}
)
.on("error", (e) => {
callbackFn({
id: "",
progress: 0,
status: "struggling",
dlUrl: null,
savePath: null,
message: `连接失败, 正在尝试 API 域名 ${
apiInfo.domains[apiInfo.domains.indexOf(apiDomain) + 1]
} ...`,
errorObj: e,
});
setTimeout(() => {
resolveHttpRequest({
success: false,
errorObj: e,
});
}, 1000);
});
}
)
.on("error", (e) => {
resolve({
success: false,
data: null,
errorObj: e,
});
});
const requestResult = await reqPromise;
if (requestResult.success) {
resolveGetVerReq({
success: true,
data: requestResult.data,
});
break;
} else {
continue;
}
}
resolveGetVerReq({
success: false,
data: null,
});
});
const rawResInfo = await getVerPromise;
@@ -109,30 +167,12 @@ const functions = {
status: "failed",
dlUrl: null,
savePath: null,
message: "未能获取 PLS 版本信息",
errorObj: rawResInfo.errorObj ? rawResInfo.errorObj : null,
message: "未能获取 PLS 版本信息, 所有 API 域名均无法连接",
});
return false;
}
try {
plsVersionInfo = JSON.parse(rawResInfo.data);
} catch (e) {
callbackFn({
id: "",
progress: 0,
status: "failed",
dlUrl: null,
savePath: null,
message: "PLS 版本信息解析失败",
errorObj: e,
});
console.error(
"[HugoAura / IPC / PLS] Error querying PLS version info:",
e
);
return false;
}
const plsVersionInfo = rawResInfo.data;
let deviceArch = process.env.PROCESSOR_ARCHITEW6432
? process.env.PROCESSOR_ARCHITEW6432
@@ -155,7 +195,16 @@ const functions = {
fsComposables.downloadFile(
plsVersionInfo.data.downloadUrl[deviceArch],
binPath,
callbackFn
(...args) => {
if (args[0].status === "done") {
if (global.__HUGO_AURA__.plsStats) {
global.__HUGO_AURA__.plsStats.installed = true;
global.__HUGO_AURA__.plsStats.status = "dead";
}
}
callbackFn(...args);
}
);
},
};
@@ -192,11 +241,18 @@ const applyPlsIpcHandler = (ipcMain) => {
(_event, _arg) => {
try {
const result = fs.existsSync(PLS_BIN_PATH);
if (global.__HUGO_AURA__.plsStats?.status === "notInstalled") {
global.__HUGO_AURA__.plsStats.status = "dead";
}
return {
success: true,
data: { isExists: result },
};
} catch (e) {
// @ts-expect-error
global.__HUGO_AURA__.plsStats.status = "notInstalled";
return {
success: false,
data: { isExists: false },