Skip to content

Commit d338cdd

Browse files
committed
feat: add custom addon path functionality and enhance GPU acceleration settings
1 parent c3f270f commit d338cdd

File tree

13 files changed

+437
-79
lines changed

13 files changed

+437
-79
lines changed

main/helpers/addonManager.ts

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ export function registerInstalledAddon(
172172
}
173173

174174
/**
175-
* 选择加速包版本
175+
* 选择加速包版本(与自定义路径互斥)
176176
*/
177177
export function selectAddonVersion(version: CudaVersion | null): void {
178178
const config = getAddonConfig();
@@ -183,10 +183,56 @@ export function selectAddonVersion(version: CudaVersion | null): void {
183183
}
184184

185185
config.selectedVersion = version;
186+
// 选择版本时清除自定义路径(互斥)
187+
if (version) {
188+
config.customAddonPath = null;
189+
}
186190
saveAddonConfig(config);
187191
logMessage(`Selected addon version: ${version}`, 'info');
188192
}
189193

194+
/**
195+
* 设置自定义 addon.node 文件路径(与版本选择互斥)
196+
*/
197+
export function setCustomAddonPath(filePath: string | null): void {
198+
const config = getAddonConfig();
199+
200+
if (filePath) {
201+
// 验证文件是否存在
202+
if (!fs.existsSync(filePath)) {
203+
throw new Error(`File not found: ${filePath}`);
204+
}
205+
// 验证文件扩展名
206+
if (!filePath.endsWith('.node')) {
207+
throw new Error('File must have .node extension');
208+
}
209+
}
210+
211+
config.customAddonPath = filePath;
212+
// 设置自定义路径时清除版本选择(互斥)
213+
if (filePath) {
214+
config.selectedVersion = null;
215+
}
216+
saveAddonConfig(config);
217+
logMessage(`Custom addon path set: ${filePath}`, 'info');
218+
}
219+
220+
/**
221+
* 获取自定义 addon.node 文件路径
222+
*/
223+
export function getCustomAddonPath(): string | null {
224+
const config = getAddonConfig();
225+
const customPath = config.customAddonPath;
226+
227+
// 验证路径是否仍然有效
228+
if (customPath && !fs.existsSync(customPath)) {
229+
logMessage(`Custom addon path no longer exists: ${customPath}`, 'warning');
230+
return customPath; // 仍然返回路径,让 UI 层决定如何处理
231+
}
232+
233+
return customPath || null;
234+
}
235+
190236
/**
191237
* 获取当前选中的加速包版本
192238
*/
@@ -355,14 +401,17 @@ export function getAddonSummary(): {
355401
selectedVersion: CudaVersion | null;
356402
installedCount: number;
357403
installedVersions: CudaVersion[];
404+
customAddonPath: string | null;
358405
} {
359406
const installed = getInstalledAddons();
360407
const selected = getSelectedAddonVersion();
408+
const customPath = getCustomAddonPath();
361409

362410
return {
363411
hasInstalled: installed.length > 0,
364412
selectedVersion: selected,
365413
installedCount: installed.length,
366414
installedVersions: installed.map((i) => i.version),
415+
customAddonPath: customPath,
367416
};
368417
}

main/helpers/addonVersions.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,15 @@ function shouldForceUpdate(): boolean {
123123
return process.env.DEV_FORCE_ADDON_UPDATE === 'true';
124124
}
125125

126+
/**
127+
* 标准化版本号格式
128+
* 统一将分隔符转换为点号,以便正确比较
129+
* 例如 "2026-02-06" -> "2026.02.06"
130+
*/
131+
function normalizeVersion(version: string): string {
132+
return version.replace(/-/g, '.');
133+
}
134+
126135
/**
127136
* 检查指定版本是否有更新
128137
*/
@@ -154,7 +163,10 @@ export async function checkVersionUpdate(
154163
}
155164

156165
const remoteInfo = remoteVersions[version];
157-
const hasUpdate = remoteInfo.version > installedInfo.remoteVersion;
166+
// 标准化版本号格式后再比较,避免 "2026.02.06" vs "2026-02-06" 因分隔符不同导致误判
167+
const normalizedRemote = normalizeVersion(remoteInfo.version);
168+
const normalizedLocal = normalizeVersion(installedInfo.remoteVersion);
169+
const hasUpdate = normalizedRemote > normalizedLocal;
158170

159171
return {
160172
cudaVersion: version,

main/helpers/ipcAddonHandlers.ts

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ipcMain, BrowserWindow } from 'electron';
1+
import { ipcMain, BrowserWindow, dialog } from 'electron';
22
import { logMessage } from './storeManager';
33
import { getCudaEnvironment, isPlatformCudaCapable } from './cudaUtils';
44
import {
@@ -9,6 +9,8 @@ import {
99
removeAddon,
1010
registerInstalledAddon,
1111
getAddonSummary,
12+
setCustomAddonPath,
13+
getCustomAddonPath,
1214
} from './addonManager';
1315
import {
1416
getAddonDownloader,
@@ -110,7 +112,8 @@ export function registerAddonIpcHandlers(): void {
110112
const remoteInfo = await getRemoteVersionInfo(config.cudaVersion);
111113
registerInstalledAddon(
112114
config.cudaVersion,
113-
remoteInfo?.version || new Date().toISOString().split('T')[0],
115+
remoteInfo?.version ||
116+
new Date().toISOString().split('T')[0].replace(/-/g, '.'),
114117
);
115118
// 自动选中刚下载的版本
116119
selectAddonVersion(config.cudaVersion);
@@ -219,5 +222,54 @@ export function registerAddonIpcHandlers(): void {
219222
},
220223
);
221224

225+
// 选择自定义 addon.node 文件
226+
ipcMain.handle('select-addon-file', async () => {
227+
try {
228+
const result = await dialog.showOpenDialog({
229+
properties: ['openFile'],
230+
title: 'Select addon.node file',
231+
filters: [
232+
{
233+
name: 'Node Addon',
234+
extensions: ['node'],
235+
},
236+
],
237+
});
238+
239+
if (result.canceled || !result.filePaths[0]) {
240+
return { filePath: null, canceled: true };
241+
}
242+
243+
return { filePath: result.filePaths[0], canceled: false };
244+
} catch (error) {
245+
logMessage(`Error selecting addon file: ${error}`, 'error');
246+
return { filePath: null, canceled: true, error: String(error) };
247+
}
248+
});
249+
250+
// 设置自定义 addon.node 路径
251+
ipcMain.handle(
252+
'set-custom-addon-path',
253+
async (event, filePath: string | null) => {
254+
try {
255+
setCustomAddonPath(filePath);
256+
return { success: true };
257+
} catch (error) {
258+
logMessage(`Error setting custom addon path: ${error}`, 'error');
259+
return { success: false, error: String(error) };
260+
}
261+
},
262+
);
263+
264+
// 获取自定义 addon.node 路径
265+
ipcMain.handle('get-custom-addon-path', async () => {
266+
try {
267+
return getCustomAddonPath();
268+
} catch (error) {
269+
logMessage(`Error getting custom addon path: ${error}`, 'error');
270+
return null;
271+
}
272+
});
273+
222274
logMessage('Addon IPC handlers registered', 'info');
223275
}

main/helpers/updater.ts

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -113,27 +113,27 @@ export function setupAutoUpdater(mainWindow: BrowserWindow) {
113113
});
114114

115115
// 当自动更新出错时,提供手动下载选项
116-
if (process.platform === 'darwin') {
117-
// 针对Mac平台的特殊处理
118-
dialog
119-
.showMessageBox(mainWindow, {
120-
type: 'info',
121-
title: '更新失败',
122-
message: '自动更新失败',
123-
detail:
124-
'由于macOS系统限制,自动更新失败。您可以手动下载并安装最新版本。',
125-
buttons: ['手动下载', '取消'],
126-
cancelId: 1,
127-
})
128-
.then(({ response }) => {
129-
if (response === 0) {
130-
// 打开GitHub发布页面,让用户手动下载
131-
const releaseUrl =
132-
'https://github.com/buxuku/SmartSub/releases/latest';
133-
require('electron').shell.openExternal(releaseUrl);
134-
}
135-
});
136-
}
116+
// if (process.platform === 'darwin') {
117+
// // 针对Mac平台的特殊处理
118+
// dialog
119+
// .showMessageBox(mainWindow, {
120+
// type: 'info',
121+
// title: '更新失败',
122+
// message: '自动更新失败',
123+
// detail:
124+
// '由于macOS系统限制,自动更新失败。您可以手动下载并安装最新版本。',
125+
// buttons: ['手动下载', '取消'],
126+
// cancelId: 1,
127+
// })
128+
// .then(({ response }) => {
129+
// if (response === 0) {
130+
// // 打开GitHub发布页面,让用户手动下载
131+
// const releaseUrl =
132+
// 'https://github.com/buxuku/SmartSub/releases/latest';
133+
// require('electron').shell.openExternal(releaseUrl);
134+
// }
135+
// });
136+
// }
137137
});
138138

139139
// 设置IPC处理程序

main/helpers/whisper.ts

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
getAddonVersionDir,
1313
hasDependentLibs,
1414
isAddonInstalled,
15+
getCustomAddonPath,
1516
} from './addonManager';
1617

1718
export const getPath = (key?: string) => {
@@ -288,34 +289,58 @@ export async function loadWhisperAddon(model: string) {
288289

289290
// 检查是否是可能支持 CUDA 的平台
290291
if (isPlatformCudaCapable() && useCuda) {
291-
// 获取用户选择的加速包版本
292-
const selectedVersion = getSelectedAddonVersion();
293-
294-
if (selectedVersion && isAddonInstalled(selectedVersion)) {
295-
// 从用户数据目录加载
296-
const versionDir = getAddonVersionDir(selectedVersion);
297-
const userAddonPath = path.join(versionDir, 'addon.node');
298-
299-
if (fs.existsSync(userAddonPath)) {
300-
// 检查并设置依赖库路径(必须在 dlopen 之前)
301-
if (hasDependentLibs(versionDir)) {
302-
setupLibraryPath(versionDir);
303-
}
292+
// 优先检查自定义 addon.node 路径
293+
const customPath = getCustomAddonPath();
294+
295+
if (customPath && fs.existsSync(customPath)) {
296+
// 使用自定义路径
297+
const customDir = path.dirname(customPath);
298+
if (hasDependentLibs(customDir)) {
299+
setupLibraryPath(customDir);
300+
}
301+
addonPath = customPath;
302+
logMessage(`Loading custom addon from: ${addonPath}`, 'info');
303+
} else if (customPath) {
304+
// 自定义路径已设置但文件不存在
305+
logMessage(
306+
`Custom addon path not found: ${customPath}, falling back to default`,
307+
'warning',
308+
);
309+
addonPath = path.join(getExtraResourcesPath(), 'addons', 'addon.node');
310+
} else {
311+
// 获取用户选择的加速包版本
312+
const selectedVersion = getSelectedAddonVersion();
313+
314+
if (selectedVersion && isAddonInstalled(selectedVersion)) {
315+
// 从用户数据目录加载
316+
const versionDir = getAddonVersionDir(selectedVersion);
317+
const userAddonPath = path.join(versionDir, 'addon.node');
318+
319+
if (fs.existsSync(userAddonPath)) {
320+
// 检查并设置依赖库路径(必须在 dlopen 之前)
321+
if (hasDependentLibs(versionDir)) {
322+
setupLibraryPath(versionDir);
323+
}
304324

305-
addonPath = userAddonPath;
306-
logMessage(`Loading CUDA addon from userData: ${addonPath}`, 'info');
325+
addonPath = userAddonPath;
326+
logMessage(`Loading CUDA addon from userData: ${addonPath}`, 'info');
327+
} else {
328+
// 用户数据目录的 addon 不存在,回退到默认版本
329+
logMessage(
330+
`Selected addon version ${selectedVersion} not found, falling back to default`,
331+
'warning',
332+
);
333+
addonPath = path.join(
334+
getExtraResourcesPath(),
335+
'addons',
336+
'addon.node',
337+
);
338+
}
307339
} else {
308-
// 用户数据目录的 addon 不存在,回退到默认版本
309-
logMessage(
310-
`Selected addon version ${selectedVersion} not found, falling back to default`,
311-
'warning',
312-
);
340+
// 没有安装加速包,使用默认的 no-cuda 版本
313341
addonPath = path.join(getExtraResourcesPath(), 'addons', 'addon.node');
342+
logMessage('No CUDA addon installed, using default addon', 'info');
314343
}
315-
} else {
316-
// 没有安装加速包,使用默认的 no-cuda 版本
317-
addonPath = path.join(getExtraResourcesPath(), 'addons', 'addon.node');
318-
logMessage('No CUDA addon installed, using default addon', 'info');
319344
}
320345
} else if (
321346
platform === 'darwin' &&

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"private": true,
33
"name": "smartsub",
44
"description": "视频转字幕,字幕翻译软件",
5-
"version": "2.12.0-beta.0",
5+
"version": "2.14.0-beta.0",
66
"author": "buxuku <buxuku@gmail.com>",
77
"main": "app/background.js",
88
"scripts": {

0 commit comments

Comments
 (0)