diff --git a/src/i18n/main.ts b/src/i18n/main.ts index 129dca8..eb62d70 100644 --- a/src/i18n/main.ts +++ b/src/i18n/main.ts @@ -18,9 +18,13 @@ const mainI18n = { }, t(key: string) { const keys = key.split('.'); - let current: any = messages[this.currentLocale]; + // 未知/非法 locale 时回退默认语言,避免 messages[locale] 为 undefined 导致崩溃 + let current: any = messages[this.currentLocale] ?? messages[DEFAULT_LANGUAGE as Language]; + if (current == null) { + return key; + } for (const k of keys) { - if (current[k] === undefined) { + if (current == null || current[k] === undefined) { // 如果找不到翻译,返回键名 return key; } diff --git a/src/main/modules/cache.ts b/src/main/modules/cache.ts index f45a07c..4dee06b 100644 --- a/src/main/modules/cache.ts +++ b/src/main/modules/cache.ts @@ -413,7 +413,9 @@ class DiskCacheManager { cleanupPolicy }; - this.saveConfig(normalizedConfig); + // 注意:getCacheConfig 是纯读取,处于播放/下载/歌词等多个热路径。 + // 此处不再落盘(electron-store.set 每次整文件写 config.json,会造成写放大与文件争用), + // 持久化交由 updateCacheConfig / setCacheDirectory 等真正的写操作完成。 return normalizedConfig; } diff --git a/src/main/modules/downloadManager.ts b/src/main/modules/downloadManager.ts index 5373bb9..1f960e0 100644 --- a/src/main/modules/downloadManager.ts +++ b/src/main/modules/downloadManager.ts @@ -484,13 +484,17 @@ class DownloadManager { } // Start download + // 注意:axios 默认只接受 2xx,403/410 会直接抛错进入 catch,导致下方"直链过期重新解析" + // 分支永远走不到。这里放行 403/410,让过期直链能触发重新解析(尤其是重启后恢复队列时)。 const response = await axios({ url: task.url, method: 'GET', responseType: 'stream', timeout: 30000, signal: controller.signal, - headers + headers, + validateStatus: (status) => + (status >= 200 && status < 300) || status === 403 || status === 410 }); // Handle response status @@ -498,6 +502,8 @@ class DownloadManager { if (status === 403 || status === 410) { // URL expired, request re-resolution from renderer + // 排空未消费的错误响应流,避免连接悬挂 + response.data?.destroy?.(); this.sendToRenderer('download:request-url', { taskId: task.taskId, songInfo: task.songInfo diff --git a/src/main/modules/loginWindow.ts b/src/main/modules/loginWindow.ts index 58b6afd..6bf63ae 100644 --- a/src/main/modules/loginWindow.ts +++ b/src/main/modules/loginWindow.ts @@ -6,7 +6,6 @@ import i18n from '../../i18n/main'; let loginWindow: BrowserWindow | null = null; const loginUrl = 'https://music.163.com/#/login/'; -const loginTitle = i18n.global.t('login.qrTitle'); /** * 打开登录窗口获取Cookie @@ -29,7 +28,8 @@ const openLoginWindow = async (mainWin: BrowserWindow) => { loginWindow = new BrowserWindow({ parent: mainWin, - title: loginTitle, + // 在打开窗口时求值,确保跟随当前语言(模块加载时 i18n locale 可能尚未设置) + title: i18n.global.t('login.qrTitle'), width: 1280, height: 800, center: true, diff --git a/src/main/modules/lxMusicHttp.ts b/src/main/modules/lxMusicHttp.ts index 2dd1842..bcd713d 100644 --- a/src/main/modules/lxMusicHttp.ts +++ b/src/main/modules/lxMusicHttp.ts @@ -42,6 +42,8 @@ export const initLxMusicHttp = () => { // 保存取消控制器 abortControllers.set(requestId, controller); + let timeoutId: ReturnType | null = null; + try { console.log(`[LxMusicHttp] 请求: ${options.method || 'GET'} ${url}`); @@ -77,13 +79,14 @@ export const initLxMusicHttp = () => { // 设置超时 const timeout = options.timeout || 30000; - const timeoutId = setTimeout(() => { + timeoutId = setTimeout(() => { console.warn(`[LxMusicHttp] 请求超时: ${url}`); controller.abort(); }, timeout); const response = await fetch(url, fetchOptions); clearTimeout(timeoutId); + timeoutId = null; console.log(`[LxMusicHttp] 响应: ${response.status} ${url}`); @@ -122,7 +125,10 @@ export const initLxMusicHttp = () => { console.error(`[LxMusicHttp] 请求失败: ${url}`, error.message); throw error; } finally { - // 清理取消控制器 + // 清理超时定时器(fetch 出错时前面来不及 clear)与取消控制器 + if (timeoutId) { + clearTimeout(timeoutId); + } abortControllers.delete(requestId); } }