diff --git a/BillNote_frontend/src-tauri/tauri.conf.json b/BillNote_frontend/src-tauri/tauri.conf.json index f20bccb..6d1079f 100644 --- a/BillNote_frontend/src-tauri/tauri.conf.json +++ b/BillNote_frontend/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", "productName": "BiliNote", - "version": "2.4.2", + "version": "2.4.3", "identifier": "com.jefferyhuang.bilinote", "build": { "frontendDist": "../dist", diff --git a/BillNote_frontend/src/pages/SettingPage/transcriber.tsx b/BillNote_frontend/src/pages/SettingPage/transcriber.tsx index 74a5669..0779b1e 100644 --- a/BillNote_frontend/src/pages/SettingPage/transcriber.tsx +++ b/BillNote_frontend/src/pages/SettingPage/transcriber.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback } from 'react' +import { useState, useEffect, useCallback, useRef } from 'react' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' @@ -40,6 +40,9 @@ export default function Transcriber() { const [newModelName, setNewModelName] = useState('') const [newModelTarget, setNewModelTarget] = useState('') const [addingModel, setAddingModel] = useState(false) + // 已提示过的下载失败 key(whisper 用 model_size,mlx 用 mlx-{size})。 + // null 表示尚未首次加载——首次加载只建立基线、不对历史失败弹窗。 + const prevFailedRef = useRef | null>(null) // 重新拉取配置(不重置用户当前的选择),用于增删自定义模型后刷新下拉与列表 const reloadConfig = useCallback(async () => { @@ -56,6 +59,23 @@ export default function Transcriber() { setModelStatuses(data.whisper) setMlxModelStatuses(data.mlx_whisper) setMlxAvailable(data.mlx_available) + + // 下载失败主动提示:只对「本次新出现的失败」弹一次,避免轮询期间反复弹窗 + const failedNow = new Map() + data.whisper.forEach(m => m.failed && failedNow.set(m.model_size, m)) + data.mlx_whisper.forEach(m => m.failed && failedNow.set(`mlx-${m.model_size}`, m)) + if (prevFailedRef.current === null) { + // 首次加载:建立基线,不对进入页面前就已失败的项弹窗(仍会在列表里红字展示) + prevFailedRef.current = new Set(failedNow.keys()) + } else { + failedNow.forEach((m, key) => { + if (!prevFailedRef.current!.has(key)) { + const detail = m.error ? `:${m.error.slice(0, 120)}` : '' + toast.error(`模型 ${m.model_size} 下载失败${detail}`, { duration: 6000 }) + } + }) + prevFailedRef.current = new Set(failedNow.keys()) + } } catch { // 静默失败,不阻塞主流程 } @@ -290,32 +310,44 @@ export default function Transcriber() { {currentModels.map(model => (
-
- {model.model_size} - {model.downloaded ? ( - - 已下载 - - ) : model.downloading ? ( - - - 下载中 - - ) : ( - 未下载 +
+
+ {model.model_size} + {model.downloaded ? ( + + 已下载 + + ) : model.downloading ? ( + + + 下载中 + + ) : model.failed ? ( + + + 下载失败 + + ) : ( + 未下载 + )} +
+ {!model.downloaded && !model.downloading && ( + )}
- {!model.downloaded && !model.downloading && ( - + {model.failed && model.error && ( +

+ {model.error} +

)}
))} @@ -368,10 +400,18 @@ export default function Transcriber() { {status?.downloading && ( )} + {status?.failed && ( + + )}
{target}
+ {status?.failed && status?.error && ( +
+ {status.error} +
+ )}