# Meetily Retranscription Workflow: How to Reprocess Stored Audio with Different Settings

> Explore Meetily's retranscription workflow to reprocess stored audio with new settings like language or models. Avoid re-recording and update transcripts efficiently.

- Repository: [Zackriya Solutions/meetily](https://github.com/Zackriya-Solutions/meetily)
- Tags: how-to-guide
- Published: 2026-07-07

---

**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](https://github.com/Zackriya-Solutions/meetily) is implemented entirely in the Rust Tauri core under [`frontend/src-tauri/src/audio/retransmission.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/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.

```rust
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.

```rust
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`](https://github.com/Zackriya-Solutions/meetily/blob/main/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`](https://github.com/Zackriya-Solutions/meetily/blob/main/metadata.json) file (lines 724–765) containing:
- `retranscribed_at` timestamp
- `status` completion flag
- `source="retranscription"` to distinguish from live transcripts

The final [`transcripts.json`](https://github.com/Zackriya-Solutions/meetily/blob/main/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 message
- `retranscription-complete` – Signals successful finish
- `retranscription-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:

```typescript
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:

```typescript
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

```typescript
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`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/retransmission.rs) | Core implementation: guard logic, VAD, engine selection, database writes |
| [`frontend/src-tauri/src/lib.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/lib.rs) | Tauri command registration (`start_…`, `cancel_…`, `is_…`) |
| [`frontend/src-tauri/src/audio/common.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/common.rs) | Shared utilities including `unload_engine_after_batch` |
| [`frontend/src-tauri/src/audio/constants.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/constants.rs) | `AUDIO_EXTENSIONS` list for file discovery |
| [`frontend/src-tauri/src/audio/decoder.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/audio/decoder.rs) | Audio decoding and resampling logic |
| [`frontend/src-tauri/src/whisper_engine/whisper_engine.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/whisper_engine/whisper_engine.rs) | Whisper engine implementation |
| [`frontend/src-tauri/src/parakeet_engine/parakeet_engine.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/parakeet_engine/parakeet_engine.rs) | Parakeet engine alternative |
| [`frontend/src-tauri/src/database/mod.rs`](https://github.com/Zackriya-Solutions/meetily/blob/main/frontend/src-tauri/src/database/mod.rs) | SQLx database layer for transcript storage |

## Summary

- **Atomic concurrency control** via `RetranscriptionGuard` prevents 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`](https://github.com/Zackriya-Solutions/meetily/blob/main/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.