翻译调整
This commit is contained in:
parent
ab28fd225f
commit
4798647b40
@ -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<SubtitleSegment>, 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))
|
||||
});
|
||||
}
|
||||
|
||||
@ -70,11 +70,17 @@ impl Translator {
|
||||
Ok(Self { client, config })
|
||||
}
|
||||
|
||||
pub async fn translate_segments(
|
||||
pub async fn translate_segments_with_progress<LF, SF>(
|
||||
&self,
|
||||
segments: &[SubtitleSegment],
|
||||
target_language: &TargetLanguage,
|
||||
) -> Result<Vec<SubtitleSegment>> {
|
||||
mut log: LF,
|
||||
mut emit_segment: SF,
|
||||
) -> Result<Vec<SubtitleSegment>>
|
||||
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::<Vec<_>>()
|
||||
.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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@ -241,6 +241,9 @@ async function handleExport(format: 'srt' | 'vtt' | 'ass') {
|
||||
<div class="toolbar-title">
|
||||
<strong>CrossSubtitle</strong>
|
||||
<span>桌面字幕工作台</span>
|
||||
<span class="credit-line">
|
||||
作者:<a href="https://kuraa.cc" target="_blank" rel="noreferrer">kuraa</a> gpt5.4
|
||||
</span>
|
||||
</div>
|
||||
<div class="toolbar-actions">
|
||||
<button class="button" type="button" :disabled="pending" @click="handlePickFiles">
|
||||
|
||||
@ -12,7 +12,13 @@ const emit = defineEmits<{
|
||||
export: [format: 'srt' | 'vtt' | 'ass']
|
||||
}>()
|
||||
|
||||
const segments = computed(() => props.task?.segments ?? [])
|
||||
const segments = computed(() =>
|
||||
[...(props.task?.segments ?? [])].sort((left, right) => {
|
||||
if (left.start !== right.start) return left.start - right.start
|
||||
if (left.end !== right.end) return left.end - right.end
|
||||
return left.id.localeCompare(right.id)
|
||||
}),
|
||||
)
|
||||
const logsExpanded = ref(false)
|
||||
|
||||
function formatTime(seconds: number) {
|
||||
|
||||
@ -14,6 +14,14 @@ import type {
|
||||
|
||||
type ExportFormat = 'srt' | 'vtt' | 'ass'
|
||||
|
||||
function sortSegments(segments: SubtitleSegment[]) {
|
||||
segments.sort((left, right) => {
|
||||
if (left.start !== right.start) return left.start - right.start
|
||||
if (left.end !== right.end) return left.end - right.end
|
||||
return left.id.localeCompare(right.id)
|
||||
})
|
||||
}
|
||||
|
||||
export const useTaskStore = defineStore('tasks', {
|
||||
state: () => ({
|
||||
tasks: [] as SubtitleTask[],
|
||||
@ -53,6 +61,7 @@ export const useTaskStore = defineStore('tasks', {
|
||||
} else {
|
||||
task.segments.push(payload.segment)
|
||||
}
|
||||
sortSegments(task.segments)
|
||||
})
|
||||
|
||||
const resetSegmentsUnlisten = await listen<ResetSegmentsEvent>('task:segments_reset', ({ payload }) => {
|
||||
@ -79,6 +88,7 @@ export const useTaskStore = defineStore('tasks', {
|
||||
})
|
||||
|
||||
const doneUnlisten = await listen<SubtitleTask>('task:done', ({ payload }) => {
|
||||
sortSegments(payload.segments)
|
||||
const index = this.tasks.findIndex((item) => item.id === payload.id)
|
||||
if (index >= 0) {
|
||||
this.tasks[index] = payload
|
||||
|
||||
@ -89,6 +89,22 @@ textarea {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.credit-line {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.credit-line a {
|
||||
color: #0f172a;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.credit-line a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.workspace-toolbar,
|
||||
.advanced-shell {
|
||||
margin-top: 12px;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user