Stream transcript segments to frontend as they are transcribed
Send each segment to the frontend immediately after transcription via a new pipeline.segment IPC message, then send speaker assignments as a batch pipeline.speaker_update message after diarization completes. This lets the UI display segments progressively instead of waiting for the entire pipeline to finish. Changes: - Add partial_segment_message and speaker_update_message IPC factories - Add on_segment callback parameter to TranscribeService.transcribe() - Emit partial segments and speaker updates from PipelineService.run() - Add send_and_receive_with_progress to SidecarManager (Rust) - Route pipeline.segment/speaker_update events in run_pipeline command - Listen for streaming events in Svelte frontend (+page.svelte) - Add tests for new message types, callback signature, and update logic Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { invoke, convertFileSrc } from '@tauri-apps/api/core';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import { open, save } from '@tauri-apps/plugin-dialog';
|
||||
import WaveformPlayer from '$lib/components/WaveformPlayer.svelte';
|
||||
import TranscriptEditor from '$lib/components/TranscriptEditor.svelte';
|
||||
@@ -85,11 +86,77 @@
|
||||
audioUrl = convertFileSrc(filePath);
|
||||
waveformPlayer?.loadAudio(audioUrl);
|
||||
|
||||
// Clear previous results
|
||||
segments.set([]);
|
||||
speakers.set([]);
|
||||
|
||||
// Start pipeline (transcription + diarization)
|
||||
isTranscribing = true;
|
||||
transcriptionProgress = 0;
|
||||
transcriptionStage = 'Starting...';
|
||||
|
||||
const unlistenSegment = await listen<{
|
||||
index: number;
|
||||
text: string;
|
||||
start_ms: number;
|
||||
end_ms: number;
|
||||
words: Array<{ word: string; start_ms: number; end_ms: number; confidence: number }>;
|
||||
}>('pipeline-segment', (event) => {
|
||||
const seg = event.payload;
|
||||
const newSeg: Segment = {
|
||||
id: `seg-${seg.index}`,
|
||||
project_id: '',
|
||||
media_file_id: '',
|
||||
speaker_id: null,
|
||||
start_ms: seg.start_ms,
|
||||
end_ms: seg.end_ms,
|
||||
text: seg.text,
|
||||
original_text: null,
|
||||
confidence: null,
|
||||
is_edited: false,
|
||||
edited_at: null,
|
||||
segment_index: seg.index,
|
||||
words: seg.words.map((w, widx) => ({
|
||||
id: `word-${seg.index}-${widx}`,
|
||||
segment_id: `seg-${seg.index}`,
|
||||
word: w.word,
|
||||
start_ms: w.start_ms,
|
||||
end_ms: w.end_ms,
|
||||
confidence: w.confidence,
|
||||
word_index: widx,
|
||||
})),
|
||||
};
|
||||
segments.update(segs => [...segs, newSeg]);
|
||||
});
|
||||
|
||||
const unlistenSpeaker = await listen<{
|
||||
updates: Array<{ index: number; speaker: string }>;
|
||||
}>('pipeline-speaker-update', (event) => {
|
||||
const { updates } = event.payload;
|
||||
// Build speakers from unique labels
|
||||
const uniqueLabels = [...new Set(updates.map(u => u.speaker))].sort();
|
||||
const newSpeakers: Speaker[] = uniqueLabels.map((label, idx) => ({
|
||||
id: `speaker-${idx}`,
|
||||
project_id: '',
|
||||
label,
|
||||
display_name: null,
|
||||
color: speakerColors[idx % speakerColors.length],
|
||||
}));
|
||||
speakers.set(newSpeakers);
|
||||
|
||||
// Update existing segments with speaker assignments
|
||||
const speakerLookup = new Map(newSpeakers.map(s => [s.label, s.id]));
|
||||
segments.update(segs =>
|
||||
segs.map((seg, i) => {
|
||||
const update = updates.find(u => u.index === i);
|
||||
if (update) {
|
||||
return { ...seg, speaker_id: speakerLookup.get(update.speaker) ?? null };
|
||||
}
|
||||
return seg;
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
try {
|
||||
const result = await invoke<{
|
||||
segments: Array<{
|
||||
@@ -159,6 +226,8 @@
|
||||
console.error('Pipeline failed:', err);
|
||||
alert(`Pipeline failed: ${err}`);
|
||||
} finally {
|
||||
unlistenSegment();
|
||||
unlistenSpeaker();
|
||||
isTranscribing = false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user