From 2662b494c517fd9a326910ddc3d625007e48eb5c Mon Sep 17 00:00:00 2001 From: bdim404 Date: Tue, 7 Apr 2026 11:03:11 +0800 Subject: [PATCH] feat: add regenerate all previews functionality and update localization strings --- .../src/locales/en-US/audiobook.json | 7 +++- .../src/locales/ja-JP/audiobook.json | 7 +++- .../src/locales/ko-KR/audiobook.json | 7 +++- .../src/locales/zh-CN/audiobook.json | 2 ++ .../src/locales/zh-TW/audiobook.json | 7 +++- qwen3-tts-frontend/src/pages/Audiobook.tsx | 32 +++++++++++++++++++ 6 files changed, 58 insertions(+), 4 deletions(-) diff --git a/qwen3-tts-frontend/src/locales/en-US/audiobook.json b/qwen3-tts-frontend/src/locales/en-US/audiobook.json index 37a6ccf..1bc7bdb 100644 --- a/qwen3-tts-frontend/src/locales/en-US/audiobook.json +++ b/qwen3-tts-frontend/src/locales/en-US/audiobook.json @@ -85,7 +85,12 @@ "voiceDesign": "Voice #{{id}}", "noVoice": "Unassigned", "editTitle": "Edit Character: {{name}}", - "savedSuccess": "Character saved" + "savedSuccess": "Character saved", + "regeneratingPreview": "Regenerating...", + "regeneratePreview": "Regenerate Preview", + "regenerateAll": "Regenerate All Previews", + "regenerateAllDone": "All previews regenerated", + "previewNotReady": "Collecting preview..." }, "confirm": { diff --git a/qwen3-tts-frontend/src/locales/ja-JP/audiobook.json b/qwen3-tts-frontend/src/locales/ja-JP/audiobook.json index d093977..9c6989f 100644 --- a/qwen3-tts-frontend/src/locales/ja-JP/audiobook.json +++ b/qwen3-tts-frontend/src/locales/ja-JP/audiobook.json @@ -84,7 +84,12 @@ "voiceDesign": "音声 #{{id}}", "noVoice": "未割り当て", "editTitle": "キャラクターを編集:{{name}}", - "savedSuccess": "キャラクターを保存しました" + "savedSuccess": "キャラクターを保存しました", + "regeneratingPreview": "試聴を再生成中...", + "regeneratePreview": "試聴を再生成", + "regenerateAll": "試聴を一括再生成", + "regenerateAllDone": "すべての試聴を再生成しました", + "previewNotReady": "試聴を収集中..." }, "confirm": { diff --git a/qwen3-tts-frontend/src/locales/ko-KR/audiobook.json b/qwen3-tts-frontend/src/locales/ko-KR/audiobook.json index 05eb5d1..0e75399 100644 --- a/qwen3-tts-frontend/src/locales/ko-KR/audiobook.json +++ b/qwen3-tts-frontend/src/locales/ko-KR/audiobook.json @@ -84,7 +84,12 @@ "voiceDesign": "음성 #{{id}}", "noVoice": "미할당", "editTitle": "캐릭터 편집: {{name}}", - "savedSuccess": "캐릭터가 저장되었습니다" + "savedSuccess": "캐릭터가 저장되었습니다", + "regeneratingPreview": "미리듣기 재생성 중...", + "regeneratePreview": "미리듣기 재생성", + "regenerateAll": "미리듣기 일괄 재생성", + "regenerateAllDone": "모든 미리듣기가 재생성되었습니다", + "previewNotReady": "미리듣기 수집 중..." }, "confirm": { diff --git a/qwen3-tts-frontend/src/locales/zh-CN/audiobook.json b/qwen3-tts-frontend/src/locales/zh-CN/audiobook.json index f6ed35a..d35e211 100644 --- a/qwen3-tts-frontend/src/locales/zh-CN/audiobook.json +++ b/qwen3-tts-frontend/src/locales/zh-CN/audiobook.json @@ -88,6 +88,8 @@ "savedSuccess": "角色已保存", "regeneratingPreview": "重新生成试听中...", "regeneratePreview": "重生试听", + "regenerateAll": "一键重新生成试听", + "regenerateAllDone": "所有试听已重新生成", "previewNotReady": "试听收集中..." }, diff --git a/qwen3-tts-frontend/src/locales/zh-TW/audiobook.json b/qwen3-tts-frontend/src/locales/zh-TW/audiobook.json index 83466e9..9bac14c 100644 --- a/qwen3-tts-frontend/src/locales/zh-TW/audiobook.json +++ b/qwen3-tts-frontend/src/locales/zh-TW/audiobook.json @@ -84,7 +84,12 @@ "voiceDesign": "音色 #{{id}}", "noVoice": "未分配", "editTitle": "編輯角色:{{name}}", - "savedSuccess": "角色已儲存" + "savedSuccess": "角色已儲存", + "regeneratingPreview": "重新生成試聽中...", + "regeneratePreview": "重生試聽", + "regenerateAll": "一鍵重新生成試聽", + "regenerateAllDone": "所有試聽已重新生成", + "previewNotReady": "試聽收集中..." }, "confirm": { diff --git a/qwen3-tts-frontend/src/pages/Audiobook.tsx b/qwen3-tts-frontend/src/pages/Audiobook.tsx index cf08b39..d543fe7 100644 --- a/qwen3-tts-frontend/src/pages/Audiobook.tsx +++ b/qwen3-tts-frontend/src/pages/Audiobook.tsx @@ -1190,6 +1190,24 @@ function CharactersPanel({ } } + const handleRegenerateAllPreviews = async () => { + const charsWithVoice = detail?.characters.filter(c => c.voice_design_id) ?? [] + if (charsWithVoice.length === 0) return + const ids = charsWithVoice.map(c => c.id) + setRegeneratingVoices(new Set(ids)) + for (const id of ids) { + try { + await audiobookApi.regenerateCharacterPreview(project.id, id) + setVoiceKeys(prev => ({ ...prev, [id]: (prev[id] || 0) + 1 })) + } catch (e: any) { + toast.error(`${detail?.characters.find(c => c.id === id)?.name}: ${formatApiError(e)}`) + } finally { + setRegeneratingVoices(prev => { const n = new Set(prev); n.delete(id); return n }) + } + } + toast.success(t('projectCard.characters.regenerateAllDone')) + } + const charCount = detail?.characters.length ?? 0 const hasChaptersOutline = (detail?.chapters.length ?? 0) > 0 @@ -1237,6 +1255,20 @@ function CharactersPanel({ {t('projectCard.reanalyze')} )} + {!isActive && status === 'characters_ready' && (detail?.characters.some(c => c.voice_design_id) ?? false) && ( + + )} {hasChaptersOutline && (