How Meetily's Audio Import System Processes Existing Audio Files: A Technical Deep Dive
Meetily's audio import system converts pre-recorded audio files (MP4, WAV, MP3) into fully transcribed meetings through a staged, cancellable pipeline that validates, decodes, resamples, and transcribes content using the same high-quality infrastructure as live capture.
Meetily is an open-source meeting assistant that enables users to retroactively import existing recordings. The import pipeline is implemented in frontend/src-tauri/src/audio/import.rs and follows a robust async workflow that handles files up to 20 GB while providing real-time progress updates to the frontend.
Safety and Concurrency Controls
The pipeline begins with safety guards to prevent race conditions and ensure clean cancellation. The system uses two global atomic flags: IMPORT_IN_PROGRESS and IMPORT_CANCELLED. These ensure only one import runs simultaneously and allow the UI to abort jobs safely.
The ImportGuard RAII type in import.rs automatically sets the in-progress flag on construction and clears it on drop, preventing stray flags even if a panic occurs. This pattern guarantees that the IMPORT_IN_PROGRESS flag is always properly reset when the guard goes out of scope, regardless of how the function exits.
File Validation and Meeting Setup
File selection starts with the select_and_validate_audio_command function, which opens a native file dialog via tauri_plugin_dialog. The selected path undergoes rigorous validation through validate_audio_file, which checks:
- File existence and readability
- Supported extensions (defined in
AUDIO_EXTENSIONS) - File size limits (maximum 20 GB)
- Audio duration extraction
Duration detection first attempts a fast metadata probe using the symphonia crate via extract_duration_from_metadata, falling back to a full decode only when necessary. This optimization prevents unnecessary I/O for large files.
Once validated, create_meeting_folder (from audio/common.rs) generates a unique sub-directory under the user's default recordings folder and initializes a placeholder metadata.json. The source audio is then copied into this folder as audio.<ext> using std::fs::copy wrapped in tokio::spawn_blocking to avoid blocking the async runtime.
Audio Processing Pipeline
The system processes audio through three distinct stages to prepare it for transcription.
Decoding and Resampling
The decode_audio_file_with_progress function reads the copied file into an AudioDecoded struct containing sample rate, channel count, and duration. Progress callbacks emit Tauri events with the stage identifier "decoding".
Next, to_whisper_format_with_progress converts the decoded audio to 16 kHz mono PCM, the format required by Whisper. This resampling stage reports progress under the "resampling" stage name.
Voice Activity Detection
The resampled audio flows into get_speech_chunks_with_progress (implemented in audio/vad.rs). For batch imports, the VAD uses a redemption time of 2000 ms—longer than live capture—to bridge natural pauses in speech and reduce fragmentation. The function returns a vector of SpeechSegment structs containing raw samples and timestamps.
Segment Optimization
To improve transcription quality, the pipeline splits overly long segments. The split_segment_at_silence function (from audio/common.rs) divides any VAD segment exceeding 25 seconds at detected silence boundaries. This prevents truncation artifacts and ensures the transcription engine receives optimally sized chunks.
Transcription and Persistence
Engine Selection
The system supports multiple transcription backends via the provider parameter. Based on this argument, the pipeline lazily initializes either the WhisperEngine via get_or_init_whisper or the ParakeetEngine via get_or_init_parakeet. Both engines implement the same async interface, receiving speech segments and returning transcribed text with confidence scores.
Transcript Construction
As transcriptions complete, create_transcript_segments (from audio/common.rs) aggregates the results. It calculates average confidence scores across all segments and transforms the raw output into structured TranscriptSegment records ready for storage.
Database and File Output
The create_meeting_with_transcripts function persists everything within a single SQLite transaction using sqlx. It inserts a new meeting row and associated transcript rows atomically.
Simultaneously, the pipeline writes transcripts.json and metadata.json to the meeting folder using temporary files and atomic rename operations. This ensures that partial writes never corrupt the meeting data if the system crashes during export.
Progress Events and Cancellation
Throughout the pipeline, emit_progress sends import-progress events to the frontend, enabling progress bars that display stages: "copying", "decoding", "resampling", "vad", "transcribing", and "saving". On completion, the system emits an import-complete event containing the meeting ID and processing statistics.
The cancellation system checks IMPORT_CANCELLED at every stage boundary. When the user triggers cancel_import_command, the flag flips and the pipeline aborts at the next checkpoint, cleaning up the temporary meeting folder and returning an "Import cancelled" error via the import-error event. Non-fatal warnings, such as "no speech detected", propagate through import-warning events without terminating the process.
Frontend Integration
Integrating the import system requires invoking Tauri commands and listening to events. The following examples demonstrate the TypeScript implementation.
Starting an Import
To initiate processing, invoke the start_import_audio_command with the source path and metadata:
import { invoke } from '@tauri-apps/api/tauri';
const sourcePath = '/Users/alice/Downloads/meeting.mp4';
const title = 'Quarterly Review';
const language = 'en';
const model = undefined; // Uses default model
const provider = 'whisper'; // or 'parakeet'
await invoke('start_import_audio_command', {
sourcePath,
title,
language,
model,
provider,
});
This command is defined at the bottom of frontend/src-tauri/src/audio/import.rs and launches the async import job immediately.
Monitoring Progress
Listen to import-progress events to update the UI:
import { listen } from '@tauri-apps/api/event';
listen('import-progress', (event) => {
const { stage, progress_percentage, message } = event.payload as {
stage: string;
progress_percentage: number;
message: string;
};
console.log(`[${stage}] ${progress_percentage}% – ${message}`);
});
Similar listeners handle import-complete, import-error, and import-warning events to manage success states and error recovery.
Cancelling Imports
To abort an ongoing import:
await invoke('cancel_import_command');
This flips the IMPORT_CANCELLED flag, causing the pipeline to terminate gracefully at the next checkpoint.
Summary
- Meetily's audio import system processes existing MP4, WAV, and MP3 files through a staged pipeline that reuses the live-capture infrastructure while optimizing for batch processing.
- Safety mechanisms like
ImportGuardand atomic flags prevent concurrent imports and ensure clean cancellation even during panics. - Validation checks file size (≤20 GB), extensions, and duration using fast metadata probes before copying the source into a dedicated meeting folder.
- Processing stages include decoding to raw PCM, resampling to 16 kHz mono, voice-activity detection with 2000 ms redemption time, and segment splitting at 25-second boundaries.
- Transcription supports both Whisper and Parakeet engines, with results persisted atomically to SQLite and JSON files.
- Real-time communication occurs through Tauri events, providing progress updates and supporting mid-process cancellation.
Frequently Asked Questions
What audio formats does Meetily support for import?
Meetily supports common formats including MP4, WAV, and MP3. The specific extensions allowed are defined in the AUDIO_EXTENSIONS constant within frontend/src-tauri/src/audio/import.rs. The validation layer confirms format support before processing begins.
How does Meetily handle large audio files during import?
The system imposes a 20 GB file size limit validated during the initial validate_audio_file check. Large files are processed efficiently through streaming decoding and resampling, with progress events emitted at each stage to keep the UI responsive. File copying operations run in spawn_blocking threads to prevent async runtime blocking.
Can I cancel an import while it's processing?
Yes. The import pipeline checks the IMPORT_CANCELLED atomic flag at every stage boundary. Invoking cancel_import_command sets this flag, causing the pipeline to abort at the next checkpoint, clean up the temporary meeting folder, and emit an import-error event with the message "Import cancelled".
Does importing reuse the same transcription quality as live recording?
Absolutely. The import system utilizes the identical decoding, VAD, and transcription infrastructure as live capture. However, it optimizes for batch processing with a longer 2000 ms VAD redemption time to bridge natural pauses in pre-recorded content, and it splits segments longer than 25 seconds to prevent truncation artifacts.
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 →