diff --git a/src-tauri/src/task.rs b/src-tauri/src/task.rs index 1c37092..1ae9a20 100644 --- a/src-tauri/src/task.rs +++ b/src-tauri/src/task.rs @@ -208,15 +208,7 @@ async fn run_pipeline( }, |segment| { if let Ok(mut current_task) = app_state_for_segment.get_task(&task_id_for_segment) { - if let Some(existing) = current_task - .segments - .iter_mut() - .find(|item| item.id == segment.id) - { - *existing = segment.clone(); - } else { - current_task.segments.push(segment.clone()); - } + upsert_segment(&mut current_task.segments, segment.clone()); let _ = app_state_for_segment.upsert_task(current_task); } window.emit( @@ -242,7 +234,31 @@ async fn run_pipeline( .ok_or_else(|| anyhow::anyhow!("翻译模式需要填写 LLM API 配置,或设置 OPENAI_API_BASE / OPENAI_API_KEY"))?; set_status(&window, &app_state, &mut task, TaskStatus::Translating, 72.0, "正在生成译文")?; let translator = Translator::new(config)?; - segments = translator.translate_segments(&segments, &task.target_lang).await?; + let task_id_for_translate = task.id.clone(); + let app_state_for_translate = app_state.clone(); + let window_for_translate = window.clone(); + segments = translator + .translate_segments_with_progress( + &segments, + &task.target_lang, + |message| { + let _ = emit_log(&window_for_translate, &task_id_for_translate, message); + }, + |segment| { + if let Ok(mut current_task) = app_state_for_translate.get_task(&task_id_for_translate) { + upsert_segment(&mut current_task.segments, segment.clone()); + let _ = app_state_for_translate.upsert_task(current_task); + } + let _ = window_for_translate.emit( + "task:segment", + crate::models::SegmentEvent { + task_id: task_id_for_translate.clone(), + segment, + }, + ); + }, + ) + .await?; task.segments = segments.clone(); app_state.upsert_task(task.clone())?; @@ -352,3 +368,19 @@ fn emit_log(window: &Window, task_id: &str, message: String) -> Result<()> { )?; Ok(()) } + +fn upsert_segment(segments: &mut Vec, segment: SubtitleSegment) { + if let Some(existing) = segments.iter_mut().find(|item| item.id == segment.id) { + *existing = segment; + } else { + segments.push(segment); + } + + segments.sort_by(|left, right| { + left.start + .partial_cmp(&right.start) + .unwrap_or(std::cmp::Ordering::Equal) + .then_with(|| left.end.partial_cmp(&right.end).unwrap_or(std::cmp::Ordering::Equal)) + .then_with(|| left.id.cmp(&right.id)) + }); +} diff --git a/src-tauri/src/translate.rs b/src-tauri/src/translate.rs index d0b4d9e..ad218b9 100644 --- a/src-tauri/src/translate.rs +++ b/src-tauri/src/translate.rs @@ -70,11 +70,17 @@ impl Translator { Ok(Self { client, config }) } - pub async fn translate_segments( + pub async fn translate_segments_with_progress( &self, segments: &[SubtitleSegment], target_language: &TargetLanguage, - ) -> Result> { + mut log: LF, + mut emit_segment: SF, + ) -> Result> + where + LF: FnMut(String), + SF: FnMut(SubtitleSegment), + { let batch_size = self.config.batch_size.clamp(10, 15); let context_size = self.config.context_size.min(5); let mut translated = segments.to_vec(); @@ -88,13 +94,25 @@ impl Translator { let context_start = batch_start.saturating_sub(context_size); let context = &segments[context_start..batch_start]; let batch = &segments[batch_start..batch_end]; + log(format!( + "translation: batch {}-{}, segments={}", + batch_start + 1, + batch_end, + batch + .iter() + .map(|segment| segment.id.as_str()) + .collect::>() + .join(", ") + )); let rows = self .translate_batch_with_retries(context, batch, target_language_name) .await?; + log(format!("translation: batch done, translated={}", rows.len())); for row in rows { if let Some(segment) = translated.iter_mut().find(|item| item.id == row.id) { segment.translated_text = Some(row.text); + emit_segment(segment.clone()); } } } diff --git a/src-tauri/vendor/licenses_bundle/homebrew-licenses.zip b/src-tauri/vendor/licenses_bundle/homebrew-licenses.zip index 13dd891..2b3b8c0 100644 Binary files a/src-tauri/vendor/licenses_bundle/homebrew-licenses.zip and b/src-tauri/vendor/licenses_bundle/homebrew-licenses.zip differ diff --git a/src/App.vue b/src/App.vue index d1cc746..7531a9b 100644 --- a/src/App.vue +++ b/src/App.vue @@ -241,6 +241,9 @@ async function handleExport(format: 'srt' | 'vtt' | 'ass') {
CrossSubtitle 桌面字幕工作台 + + 作者:kuraa gpt5.4 +