Meetily Retranscription Workflow: How to Reprocess Stored Audio with Different Settings
Meetily's retranscription workflow lets you reprocess previously recorded meeting audio with different transcription settings—such as language, model, or provider—without re-recording, by running a background job that decodes the stored file, re-segments the audio, and atomically updates the database with new transcripts.
The retranscription feature in Zackriya-Solutions/meetily is implemented entirely in the Rust Tauri core under frontend/src-tauri/src/audio/retransmission.rs. It provides a robust pipeline that handles CPU-intensive audio processing asynchronously while keeping the UI responsive through real-time progress events.
Workflow Architecture Overview
The retranscription system exposes three main Tauri commands that orchestrate the process:
start_retranscription_command– Entry point that spawns the async background job (lines 777–795)cancel_retranscription_command– Sets a global cancellation flag to abort running jobs (lines 822–828)is_retranscription_in_progress_command– Status query for UI state management (lines 830–833)
A global atomic flag (RETRANSCRIPTION_IN_PROGRESS) prevents concurrent retranscription jobs. The system uses an RAII guard (RetranscriptionGuard) that automatically clears this flag when the job completes or panics, ensuring the system never remains locked in a "busy" state.
static RETRANSCRIPTION_IN_PROGRESS: AtomicBool = AtomicBool::new(false);
struct RetranscriptionGuard;
impl RetranscriptionGuard { /* acquire() checks the flag */ }
impl Drop for RetranscriptionGuard { /* clears the flag */ }
Step-by-Step Retranscription Process
1. Concurrency Control and Job Initiation
When start_retranscription_command is invoked, it first attempts to acquire the RetranscriptionGuard. If another retranscription is already running, it returns an error immediately. Otherwise, it spawns the heavy processing work using tauri::async_runtime::spawn and returns a "started" response to the frontend, allowing the UI to remain responsive.
tauri::async_runtime::spawn(async move {
let result = start_retransmission(...).await;
if let Err(e) = result { error!("Retranscription failed: {}", e); }
});
2. Audio Discovery and Decoding
The run_retranscription function calls find_audio_file (lines 139–168) to locate the stored audio. It first checks for common filenames like audio.mp4 or audio.wav, then falls back to scanning the meeting folder for any file with extensions defined in AUDIO_EXTENSIONS (from constants.rs).
Once found, the file is decoded using decode_audio_file (lines 199–226), which runs as a blocking task to handle CPU-intensive decoding. The output is resampled to 16 kHz mono, the standard format required by both Whisper and Parakeet engines.
3. Voice Activity Detection and Segmentation
Unlike live transcription, which uses a 400 ms redemption window, batch-mode retranscription applies a 2000 ms redemption time to bridge natural pauses in speech. This reduces fragmenting of longer utterances. The VAD process (lines 235–263) emits progress updates via emit_progress to keep the UI synchronized.
Segments longer than 25 seconds (MAX_SEGMENT_SAMPLES) are automatically split at low-energy windows (lines 313–331). This prevents truncation issues when transcribing long monologues and improves alignment accuracy.
4. Engine Selection and Transcription
Based on the provider argument passed from the frontend, the system initializes the appropriate engine:
get_or_init_whisper– Loads or reuses a Whisper engine, ensuring the requested model is active (lines 301–376)get_or_init_parakeet– Initializes the Parakeet alternative LLM-based engine (lines 386–466)
Both engines read persisted configuration from the transcript_settings database table and automatically reload models if the user requested a different one than previously cached.
Each speech segment is processed sequentially:
- Progress is mapped to the 25 %–80 % range during this phase
- Segments shorter than 100 ms are skipped
- The engine returns text with confidence scores (Whisper provides variable confidence; Parakeet uses fixed values)
5. Atomic Database Updates and Metadata
To prevent partial data corruption, all existing transcripts for the meeting are removed in a single transaction before inserting the new results (lines 426–558). If any step fails, the database rolls back to its previous state.
After successful transcription, write_retranscription_metadata updates (or creates) a metadata.json file (lines 724–765) containing:
retranscribed_attimestampstatuscompletion flagsource="retranscription"to distinguish from live transcripts
The final transcripts.json file is written alongside the metadata for external access.
6. Progress Events and Cancellation
Throughout the pipeline, the emit_progress function fires Tauri events that the frontend can observe:
retranscription-progress– Contains stage, percentage, and messageretranscription-complete– Signals successful finishretranscription-error– Contains error details
A second global atomic flag (RETRANSCRIPTION_CANCELLED) enables cancellation. Every major step—decoding, VAD, and each segment transcription—checks this flag (lines 195–199, 215–219, 344–347, 414–418) and aborts early with a clear error message if set.
Frontend Integration
Starting a Retranscription
From TypeScript, invoke the command with override parameters:
await invoke('start_retranscription_command', {
meeting_id: selectedMeeting.id,
meeting_folder_path: selectedMeeting.folderPath,
language: 'en', // optional – overrides stored language
model: 'base', // optional – overrides stored model name
provider: 'localWhisper' // "localWhisper" | "parakeet"
});
Monitoring Progress
Listen to Tauri events to drive progress bars:
listen<RetranscriptionProgress>('retranscription-progress', ev => {
const { stage, progress_percentage, message } = ev.payload;
setProgress(stage, progress_percentage, message);
});
listen('retranscription-complete', ev => {
toast.success('Retranscription finished');
reloadTranscripts();
});
listen('retranscription-error', ev => {
toast.error(`Error: ${ev.payload.error}`);
});
Canceling an Ongoing Job
await invoke('cancel_retranscription_command');
const inProgress = await invoke<boolean>('is_retranscription_in_progress_command');
Key Source Files
| File | Role |
|---|---|
frontend/src-tauri/src/audio/retransmission.rs |
Core implementation: guard logic, VAD, engine selection, database writes |
frontend/src-tauri/src/lib.rs |
Tauri command registration (start_…, cancel_…, is_…) |
frontend/src-tauri/src/audio/common.rs |
Shared utilities including unload_engine_after_batch |
frontend/src-tauri/src/audio/constants.rs |
AUDIO_EXTENSIONS list for file discovery |
frontend/src-tauri/src/audio/decoder.rs |
Audio decoding and resampling logic |
frontend/src-tauri/src/whisper_engine/whisper_engine.rs |
Whisper engine implementation |
frontend/src-tauri/src/parakeet_engine/parakeet_engine.rs |
Parakeet engine alternative |
frontend/src-tauri/src/database/mod.rs |
SQLx database layer for transcript storage |
Summary
- Atomic concurrency control via
RetranscriptionGuardprevents simultaneous jobs and ensures cleanup on panic - Batch-mode VAD uses a 2000 ms redemption window (vs 400 ms live) for better segment quality
- Automatic splitting at 25-second boundaries prevents transcription truncation
- Provider flexibility allows switching between Whisper and Parakeet engines with automatic model reloading
- Transactional safety guarantees atomic database updates—old transcripts are only deleted after new ones are ready
- Real-time feedback via Tauri events supports responsive UIs with progress bars and cancellation
Frequently Asked Questions
Can I run multiple retranscriptions simultaneously?
No. The system uses a global RETRANSCRIPTION_IN_PROGRESS atomic flag managed by an RAII RetranscriptionGuard that prevents concurrent jobs. This protects system resources and prevents model conflicts. If you attempt to start a second job while one is running, the command returns an immediate error.
What audio formats are supported for retranscription?
The find_audio_file function first looks for audio.mp4 or audio.wav, then scans for any extension defined in AUDIO_EXTENSIONS from constants.rs. The decode_audio_file function handles container decoding and resamples all audio to 16 kHz mono, supporting common formats like MP4, WAV, and others supported by the underlying decoder.
How does Meetily handle partial failures during retranscription?
The database update is wrapped in a transaction (lines 426–558). Existing transcripts are only deleted after the new transcription data is prepared, and both operations commit atomically. If any step fails—whether during decoding, VAD, or transcription—the transaction rolls back, leaving the original transcripts intact. The metadata file is only written after successful database commitment.
What is the difference between live transcription and retranscription VAD settings?
Live transcription uses a 400 ms redemption window to minimize latency for real-time captions. Retranscription uses a 2000 ms redemption time (lines 235–263) because delay is not a concern, allowing the algorithm to bridge longer natural pauses in speech. This produces fewer, longer segments that improve context for the transcription engine and reduce word fragmentation.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →