← back to dennisrongo__cut-clean

Function bodies 405 total

All specs Real LLM only Function bodies
default_theme function · rust · L36-L39 (4 LOC)
src-tauri/src/services/settings.rs
fn default_theme() -> ThemeMode {
    ThemeMode::Dark
}
get_settings_path function · rust · L40-L48 (9 LOC)
src-tauri/src/services/settings.rs
fn get_settings_path() -> Result<PathBuf, String> {
    let app_dir = dirs::data_local_dir()
        .ok_or("Failed to get app data directory")?;
    let settings_dir = app_dir.join("cutclean");
    fs::create_dir_all(&settings_dir)
        .map_err(|e| format!("Failed to create settings directory: {}", e))?;
    Ok(settings_dir.join(SETTINGS_FILE))
}
get_settings function · rust · L49-L61 (13 LOC)
src-tauri/src/services/settings.rs
pub fn get_settings() -> AppSettings {
    get_settings_path()
        .and_then(|path| {
            fs::read_to_string(&path)
                .map_err(|e| format!("Failed to read settings: {}", e))
        })
        .and_then(|json| {
            serde_json::from_str(&json)
                .map_err(|e| format!("Failed to parse settings: {}", e))
        })
        .unwrap_or_default()
}
set_theme function · rust · L62-L72 (11 LOC)
src-tauri/src/services/settings.rs
pub fn set_theme(mode: ThemeMode) -> Result<(), String> {
    let mut settings = get_settings();
    settings.theme = mode;
    let path = get_settings_path()?;
    let json = serde_json::to_string_pretty(&settings)
        .map_err(|e| format!("Failed to serialize settings: {}", e))?;
    fs::write(&path, json)
        .map_err(|e| format!("Failed to write settings: {}", e))?;
    Ok(())
}
model_name function · rust · L39-L47 (9 LOC)
src-tauri/src/services/transcription.rs
    pub fn model_name(&self) -> &str {
        match self {
            WhisperModel::Tiny => "tiny",
            WhisperModel::Base => "base",
            WhisperModel::Small => "small",
            WhisperModel::Medium => "medium",
            WhisperModel::Large => "large",
        }
    }
file_name function · rust · L50-L58 (9 LOC)
src-tauri/src/services/transcription.rs
    pub fn file_name(&self) -> &str {
        match self {
            WhisperModel::Tiny => "ggml-tiny.bin",
            WhisperModel::Base => "ggml-base.bin",
            WhisperModel::Small => "ggml-small.bin",
            WhisperModel::Medium => "ggml-medium.bin",
            WhisperModel::Large => "ggml-large.bin",
        }
    }
size_bytes function · rust · L61-L69 (9 LOC)
src-tauri/src/services/transcription.rs
    pub fn size_bytes(&self) -> u64 {
        match self {
            WhisperModel::Tiny => 74 * 1024 * 1024,
            WhisperModel::Base => 142 * 1024 * 1024,
            WhisperModel::Small => 466 * 1024 * 1024,
            WhisperModel::Medium => 1500 * 1024 * 1024,
            WhisperModel::Large => 2900 * 1024 * 1024,
        }
    }
Repobility · open methodology · https://repobility.com/research/
display_name function · rust · L80-L88 (9 LOC)
src-tauri/src/services/transcription.rs
    pub fn display_name(&self) -> &str {
        match self {
            WhisperModel::Tiny => "Tiny (74MB)",
            WhisperModel::Base => "Base (142MB)",
            WhisperModel::Small => "Small (466MB)",
            WhisperModel::Medium => "Medium (1.5GB)",
            WhisperModel::Large => "Large (2.9GB)",
        }
    }
from_str function · rust · L91-L100 (10 LOC)
src-tauri/src/services/transcription.rs
    pub fn from_str(s: &str) -> Option<Self> {
        match s.to_lowercase().as_str() {
            "tiny" => Some(WhisperModel::Tiny),
            "base" => Some(WhisperModel::Base),
            "small" => Some(WhisperModel::Small),
            "medium" => Some(WhisperModel::Medium),
            "large" => Some(WhisperModel::Large),
            _ => None,
        }
    }
default function · rust · L104-L106 (3 LOC)
src-tauri/src/services/transcription.rs
    fn default() -> Self {
        WhisperModel::Tiny  // Use Tiny by default for 3-4x faster transcription
    }
default function · rust · L186-L193 (8 LOC)
src-tauri/src/services/transcription.rs
    fn default() -> Self {
        Self {
            model: WhisperModel::Tiny,  // Use Tiny for 3-4x faster transcription
            language: Some("en".to_string()),  // Force English to skip language detection
            word_timestamps: true,
            threads: 0,  // 0 = auto (will be optimized based on audio length)
        }
    }
get_models_dir function · rust · L197-L210 (14 LOC)
src-tauri/src/services/transcription.rs
pub fn get_models_dir() -> Result<PathBuf, String> {
    let app_dir = dirs::data_local_dir()
        .ok_or("Failed to get app data directory")?;

    let models_dir = app_dir
        .join("cutclean")
        .join("whisper-models");

    // Create directory if it doesn't exist
    std::fs::create_dir_all(&models_dir)
        .map_err(|e| format!("Failed to create models directory: {}", e))?;

    Ok(models_dir)
}
get_model_path function · rust · L213-L216 (4 LOC)
src-tauri/src/services/transcription.rs
pub fn get_model_path(model: WhisperModel) -> Result<PathBuf, String> {
    let models_dir = get_models_dir()?;
    Ok(models_dir.join(model.file_name()))
}
is_model_downloaded function · rust · L219-L224 (6 LOC)
src-tauri/src/services/transcription.rs
pub fn is_model_downloaded(model: WhisperModel) -> bool {
    match get_model_path(model) {
        Ok(path) => path.exists(),
        Err(_) => false,
    }
}
download_model function · rust · L227-L327 (101 LOC)
src-tauri/src/services/transcription.rs
pub fn download_model(
    model: WhisperModel,
    app: Option<&AppHandle>,
) -> Result<PathBuf, String> {
    let model_path = get_model_path(model)?;

    // Check if already downloaded
    if model_path.exists() {
        info!("Model already exists at: {:?}", model_path);
        return Ok(model_path);
    }

    let url = model.download_url();
    info!("Downloading model from: {}", url);

    let total_size = model.size_bytes();

    // Create parent directories
    let models_dir = model_path
        .parent()
        .ok_or("Failed to get parent directory")?;
    std::fs::create_dir_all(models_dir)
        .map_err(|e| format!("Failed to create models directory: {}", e))?;

    // Download using ureq
    let response = ureq::get(&url)
        .timeout(Duration::from_secs(600))
        .call()
        .map_err(|e| format!("Failed to download model: {}", e))?;

    // Create temporary file
    let temp_path = model_path.with_extension("tmp");
    let file = File::create(&temp_pa
Repobility analyzer · published findings · https://repobility.com
get_available_models function · rust · L330-L339 (10 LOC)
src-tauri/src/services/transcription.rs
pub fn get_available_models() -> Vec<ModelInfo> {
    let models_dir = get_models_dir().unwrap_or_else(|_| PathBuf::from("."));
    vec![
        model_info(WhisperModel::Tiny, &models_dir),
        model_info(WhisperModel::Base, &models_dir),
        model_info(WhisperModel::Small, &models_dir),
        model_info(WhisperModel::Medium, &models_dir),
        model_info(WhisperModel::Large, &models_dir),
    ]
}
model_info function · rust · L351-L364 (14 LOC)
src-tauri/src/services/transcription.rs
fn model_info(model: WhisperModel, models_dir: &PathBuf) -> ModelInfo {
    let model_path = models_dir.join(model.file_name());
    let is_downloaded = model_path.exists();

    ModelInfo {
        model_type: format!("{:?}", model).to_lowercase(),
        file_name: model.file_name().to_string(),
        display_name: model.display_name().to_string(),
        size_bytes: model.size_bytes(),
        is_downloaded,
        path: is_downloaded.then(|| model_path.to_string_lossy().to_string()),
    }
}
wav_to_f32_samples function · rust · L367-L388 (22 LOC)
src-tauri/src/services/transcription.rs
fn wav_to_f32_samples(wav_data: &[u8]) -> Result<Vec<f32>, String> {
    // Validate WAV header
    if wav_data.len() < 44 || &wav_data[0..4] != b"RIFF" {
        return Err("Invalid WAV file".to_string());
    }

    // Extract audio format info
    let channels = u16::from_le_bytes([wav_data[22], wav_data[23]]) as usize;
    let bits_per_sample = u16::from_le_bytes([wav_data[34], wav_data[35]]) as usize;

    // Find data chunk
    let data_start = wav_data[12..]
        .windows(4)
        .position(|w| w == b"data")
        .map(|p| 12 + p + 8)
        .ok_or_else(|| "No data chunk found".to_string())?;

    let audio_data = &wav_data[data_start..];

    // Convert PCM to f32
    convert_pcm_to_f32(audio_data, bits_per_sample, channels)
}
convert_pcm_to_f32 function · rust · L391-L463 (73 LOC)
src-tauri/src/services/transcription.rs
fn convert_pcm_to_f32(
    audio_data: &[u8],
    bits_per_sample: usize,
    channels: usize,
) -> Result<Vec<f32>, String> {
    let samples: Vec<f32> = match bits_per_sample {
        16 => {
            let num_samples = audio_data.len() / 2;
            let mut result = Vec::with_capacity(num_samples);

            for i in 0..num_samples {
                let sample = i16::from_le_bytes([
                    audio_data[i * 2],
                    audio_data[i * 2 + 1],
                ]);
                result.push(sample as f32 / i16::MAX as f32);
            }

            result
        }
        24 => {
            let num_samples = audio_data.len() / 3;
            let mut result = Vec::with_capacity(num_samples);

            for i in 0..num_samples {
                let byte1 = audio_data[i * 3] as i32;
                let byte2 = audio_data[i * 3 + 1] as i32;
                let byte3 = audio_data[i * 3 + 2] as i32;

                let sample = if byte3 & 0x80 != 0 {
  
new function · rust · L471-L475 (5 LOC)
src-tauri/src/services/transcription.rs
    pub fn new(context: WhisperContext) -> Self {
        Self {
            context: Arc::new(context),
        }
    }
inner function · rust · L476-L479 (4 LOC)
src-tauri/src/services/transcription.rs
    pub fn inner(&self) -> &WhisperContext {
        &self.context
    }
clone function · rust · L480-L485 (6 LOC)
src-tauri/src/services/transcription.rs
    pub fn clone(&self) -> Self {
        Self {
            context: Arc::clone(&self.context),
        }
    }
get_whisper_context function · rust · L493-L532 (40 LOC)
src-tauri/src/services/transcription.rs
fn get_whisper_context(model: WhisperModel) -> Result<ContextWrapper, String> {
    unsafe {
        // Check if we need to load a different model
        if let Some(last_model) = LAST_MODEL {
            if last_model == model && WHISPER_CONTEXT.is_some() {
                return Ok(WHISPER_CONTEXT.as_ref().unwrap().clone());
            }
        }

        // Load new model
        let model_path = get_model_path(model)?;

        if !model_path.exists() {
            return Err(format!(
                "Model not found: {}. Please download it first.",
                model_path.display()
            ));
        }

        info!("Loading Whisper model from: {:?}", model_path);

        let params = WhisperContextParameters {
            use_gpu: false, // CPU-only for compatibility
            ..Default::default()
        };

        let context = WhisperContext::new_with_params(
            &model_path.to_string_lossy(),
            params,
        ).map_err(|e| format!("Failed to
If a scraper extracted this row, it came from Repobility (https://repobility.com)
transcribe_with_whisper function · rust · L535-L673 (139 LOC)
src-tauri/src/services/transcription.rs
fn transcribe_with_whisper(
    audio_data: &[f32],
    config: &TranscriptionConfig,
) -> Result<TranscriptionInternalResult, String> {
    let start_time = Instant::now();

    info!("Starting transcription with {} samples", audio_data.len());

    // Get Whisper context
    let context_wrapper = get_whisper_context(config.model)?;
    let context = context_wrapper.inner();

    // Configure parameters for word-level timestamps
    let mut params = FullParams::new(SamplingStrategy::Greedy { best_of: 1 });

    // Set language
    if let Some(ref lang) = config.language {
        params.set_language(Some(lang));
    } else {
        params.set_language(None); // Auto-detect
    }

    params.set_translate(false);

    // Optimize threads for short recordings - fewer threads reduces overhead
    // Whisper's native sample rate
    const WHISPER_SAMPLE_RATE: usize = 16000;
    let audio_duration_sec = audio_data.len() as f64 / WHISPER_SAMPLE_RATE as f64;

    let optimized_threads = if 
language_id_to_str function · rust · L683-L734 (52 LOC)
src-tauri/src/services/transcription.rs
fn language_id_to_str(id: i32) -> Option<String> {
    // whisper-rs language IDs
    match id {
        0 => Some("en".to_string()),
        1 => Some("zh".to_string()),
        2 => Some("de".to_string()),
        3 => Some("es".to_string()),
        4 => Some("ru".to_string()),
        5 => Some("ko".to_string()),
        6 => Some("fr".to_string()),
        7 => Some("ja".to_string()),
        8 => Some("pt".to_string()),
        9 => Some("tr".to_string()),
        10 => Some("pl".to_string()),
        11 => Some("ca".to_string()),
        12 => Some("nl".to_string()),
        13 => Some("ar".to_string()),
        14 => Some("sv".to_string()),
        15 => Some("it".to_string()),
        16 => Some("id".to_string()),
        17 => Some("hi".to_string()),
        18 => Some("fi".to_string()),
        19 => Some("vi".to_string()),
        20 => Some("he".to_string()),
        21 => Some("uk".to_string()),
        22 => Some("el".to_string()),
        23 => Some("ms".to_string()),
 
transcribe_audio function · rust · L737-L780 (44 LOC)
src-tauri/src/services/transcription.rs
pub fn transcribe_audio(
    audio_path: &str,
    clip_id: &str,
    config: &TranscriptionConfig,
) -> Result<TranscriptionResult, String> {
    let start_time = Instant::now();

    // Check if audio file exists
    if !Path::new(audio_path).exists() {
        return Err(format!("Audio file not found: {}", audio_path));
    }

    // Read WAV file
    let wav_data = std::fs::read(audio_path)
        .map_err(|e| format!("Failed to read audio file: {}", e))?;

    // Convert to f32 samples
    let samples = wav_to_f32_samples(&wav_data)?;

    if samples.is_empty() {
        return Ok(TranscriptionResult {
            clip_id: clip_id.to_string(),
            words: vec![],
            full_text: String::new(),
            language: "en".to_string(),
            processing_time: start_time.elapsed().as_secs_f64(),
        });
    }

    info!("Transcribing {} samples ({} seconds)", samples.len(), samples.len() / 16000);

    // Run transcription
    let internal_result = transcribe_w
transcribe_video_async function · rust · L786-L881 (96 LOC)
src-tauri/src/services/transcription.rs
pub fn transcribe_video_async(
    video_path: String,
    clip_id: String,
    app: AppHandle,
) -> Result<(), String> {
    use std::thread;

    let clip_id_clone = clip_id.clone();

    thread::spawn(move || {
        // Emit initial progress
        let _ = app.emit("transcription-progress", TranscriptionProgress {
            clip_id: clip_id_clone.clone(),
            progress: 0,
            stage: "Initializing".to_string(),
        });

        // Extract audio
        let _ = app.emit("transcription-progress", TranscriptionProgress {
            clip_id: clip_id_clone.clone(),
            progress: 10,
            stage: "Extracting audio".to_string(),
        });

        let wav_path = match crate::services::audio_extractor::extract_audio_to_temp(&video_path) {
            Ok(path) => path,
            Err(e) => {
                let _ = app.emit("transcription-error", TranscriptionError {
                    clip_id: clip_id_clone.clone(),
                    error: forma
test_whisper_model_names function · rust · L888-L894 (7 LOC)
src-tauri/src/services/transcription.rs
    fn test_whisper_model_names() {
        assert_eq!(WhisperModel::Tiny.file_name(), "ggml-tiny.bin");
        assert_eq!(WhisperModel::Base.file_name(), "ggml-base.bin");
        assert_eq!(WhisperModel::Small.file_name(), "ggml-small.bin");
        assert_eq!(WhisperModel::Medium.file_name(), "ggml-medium.bin");
        assert_eq!(WhisperModel::Large.file_name(), "ggml-large.bin");
    }
test_whisper_model_sizes function · rust · L897-L902 (6 LOC)
src-tauri/src/services/transcription.rs
    fn test_whisper_model_sizes() {
        assert!(WhisperModel::Tiny.size_bytes() < WhisperModel::Base.size_bytes());
        assert!(WhisperModel::Base.size_bytes() < WhisperModel::Small.size_bytes());
        assert!(WhisperModel::Small.size_bytes() < WhisperModel::Medium.size_bytes());
        assert!(WhisperModel::Medium.size_bytes() < WhisperModel::Large.size_bytes());
    }
test_whisper_model_from_str function · rust · L905-L909 (5 LOC)
src-tauri/src/services/transcription.rs
    fn test_whisper_model_from_str() {
        assert_eq!(WhisperModel::from_str("tiny"), Some(WhisperModel::Tiny));
        assert_eq!(WhisperModel::from_str("SMALL"), Some(WhisperModel::Small));
        assert_eq!(WhisperModel::from_str("invalid"), None);
    }
test_default_config function · rust · L912-L918 (7 LOC)
src-tauri/src/services/transcription.rs
    fn test_default_config() {
        let config = TranscriptionConfig::default();
        assert_eq!(config.model, WhisperModel::Small);
        assert_eq!(config.language, Some("en".to_string()));
        assert!(config.word_timestamps);
        assert_eq!(config.threads, 0);
    }
All rows above produced by Repobility · https://repobility.com
test_transcribed_word_structure function · rust · L921-L934 (14 LOC)
src-tauri/src/services/transcription.rs
    fn test_transcribed_word_structure() {
        let word = TranscribedWord {
            id: "test_id".to_string(),
            word: "hello".to_string(),
            start_time: 0.0,
            end_time: 0.5,
            confidence: 0.95,
        };

        assert_eq!(word.word, "hello");
        assert_eq!(word.start_time, 0.0);
        assert_eq!(word.end_time, 0.5);
        assert_eq!(word.confidence, 0.95);
    }
test_transcription_result_structure function · rust · L937-L959 (23 LOC)
src-tauri/src/services/transcription.rs
    fn test_transcription_result_structure() {
        let result = TranscriptionResult {
            clip_id: "clip1".to_string(),
            words: vec![
                TranscribedWord {
                    id: "1".to_string(),
                    word: "hello".to_string(),
                    start_time: 0.0,
                    end_time: 0.5,
                    confidence: 0.95,
                },
            ],
            full_text: "hello".to_string(),
            language: "en".to_string(),
            processing_time: 1.5,
        };

        assert_eq!(result.clip_id, "clip1");
        assert_eq!(result.words.len(), 1);
        assert_eq!(result.full_text, "hello");
        assert_eq!(result.language, "en");
        assert_eq!(result.processing_time, 1.5);
    }
test_transcription_progress_structure function · rust · L962-L972 (11 LOC)
src-tauri/src/services/transcription.rs
    fn test_transcription_progress_structure() {
        let progress = TranscriptionProgress {
            clip_id: "clip1".to_string(),
            progress: 50,
            stage: "Processing".to_string(),
        };

        assert_eq!(progress.clip_id, "clip1");
        assert_eq!(progress.progress, 50);
        assert_eq!(progress.stage, "Processing");
    }
test_transcription_serialization function · rust · L975-L989 (15 LOC)
src-tauri/src/services/transcription.rs
    fn test_transcription_serialization() {
        let result = TranscriptionResult {
            clip_id: "clip1".to_string(),
            words: vec![],
            full_text: "test".to_string(),
            language: "en".to_string(),
            processing_time: 1.0,
        };

        let json = serde_json::to_string(&result);
        assert!(json.is_ok());

        let parsed: Result<TranscriptionResult, _> = serde_json::from_str(&json.unwrap());
        assert!(parsed.is_ok());
    }
test_word_serialization function · rust · L992-L1008 (17 LOC)
src-tauri/src/services/transcription.rs
    fn test_word_serialization() {
        let word = TranscribedWord {
            id: "1".to_string(),
            word: "hello".to_string(),
            start_time: 0.0,
            end_time: 0.5,
            confidence: 0.95,
        };

        let json = serde_json::to_string(&word);
        assert!(json.is_ok());

        let parsed: Result<TranscribedWord, _> = serde_json::from_str(&json.unwrap());
        assert!(parsed.is_ok());
        let parsed_word = parsed.unwrap();
        assert_eq!(parsed_word.word, "hello");
    }
test_model_info_structure function · rust · L1011-L1019 (9 LOC)
src-tauri/src/services/transcription.rs
    fn test_model_info_structure() {
        let models_dir = PathBuf::from("/tmp/models");
        let info = model_info(WhisperModel::Tiny, &models_dir);

        assert_eq!(info.model_type, "tiny");
        assert_eq!(info.file_name, "ggml-tiny.bin");
        assert_eq!(info.display_name, "Tiny (74MB)");
        assert_eq!(info.size_bytes, 74 * 1024 * 1024);
    }
get_video_metadata function · rust · L22-L134 (113 LOC)
src-tauri/src/services/video_processor.rs
pub fn get_video_metadata(video_path: &str) -> Result<VideoMetadata, String> {
    if !Path::new(video_path).exists() {
        return Err(format!("Video file not found: {}", video_path));
    }

    // Use ffprobe to get JSON metadata
    let output = Command::new("ffprobe")
        .args([
            "-v", "quiet",
            "-print_format", "json",
            "-show_format",
            "-show_streams",
            video_path,
        ])
        .output()
        .map_err(|e| format!("Failed to run ffprobe: {}. Is FFmpeg installed and in PATH?", e))?;

    if !output.status.success() {
        let stderr = String::from_utf8_lossy(&output.stderr);
        return Err(format!("ffprobe failed: {}", stderr));
    }

    let json_str = String::from_utf8_lossy(&output.stdout);
    let value: serde_json::Value = serde_json::from_str(&json_str)
        .map_err(|e| format!("Failed to parse ffprobe output: {}", e))?;

    // Get format info
    let format = value.get("format").ok_or("No f
extract_frame function · rust · L144-L179 (36 LOC)
src-tauri/src/services/video_processor.rs
pub fn extract_frame(video_path: &str, timestamp_sec: f64) -> Result<String, String> {
    if !Path::new(video_path).exists() {
        return Err(format!("Video file not found: {}", video_path));
    }

    // Use ffmpeg to extract a single frame as PNG to stdout
    let output = Command::new("ffmpeg")
        .args([
            "-ss", &timestamp_sec.to_string(),
            "-i", video_path,
            "-vframes", "1",
            "-q:v", "2",
            "-f", "image2pipe",
            "-",
        ])
        .output()
        .map_err(|e| format!("Failed to run ffmpeg: {}", e))?;

    if !output.status.success() {
        // ffmpeg outputs to stderr even on success, check if we got any stdout data
        if output.stdout.is_empty() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(format!("ffmpeg failed: {}", stderr));
        }
    }

    // The output should be PNG data
    let png_data = output.stdout;

    if png_data.is_empty() {
   
Repobility · open methodology · https://repobility.com/research/
extract_frames function · rust · L190-L216 (27 LOC)
src-tauri/src/services/video_processor.rs
pub fn extract_frames(
    video_path: &str,
    duration_sec: f64,
    count: usize,
) -> Result<Vec<(f64, String)>, String> {
    let interval = if count > 0 {
        duration_sec / count as f64
    } else {
        1.0
    };

    let mut frames = Vec::new();

    for i in 0..count {
        let timestamp = interval * i as f64;
        match extract_frame(video_path, timestamp) {
            Ok(frame_data) => {
                frames.push((timestamp, frame_data));
            }
            Err(e) => {
                eprintln!("Failed to extract frame at {}: {}", timestamp, e);
            }
        }
    }

    Ok(frames)
}
extract_frames_async function · rust · L219-L235 (17 LOC)
src-tauri/src/services/video_processor.rs
pub fn extract_frames_async(
    video_path: String,
    duration_sec: f64,
    count: usize,
    app: AppHandle,
) {
    thread::spawn(move || {
        match extract_frames(&video_path, duration_sec, count) {
            Ok(frames) => {
                let _ = app.emit("frames-extracted", frames);
            }
            Err(e) => {
                let _ = app.emit("frame-extraction-error", e);
            }
        }
    });
}
test_get_video_metadata_file_not_found function · rust · L242-L246 (5 LOC)
src-tauri/src/services/video_processor.rs
    fn test_get_video_metadata_file_not_found() {
        let result = get_video_metadata("/nonexistent/path/to/video.mp4");
        assert!(result.is_err());
        assert!(result.unwrap_err().contains("not found"));
    }
test_extract_frame_file_not_found function · rust · L249-L252 (4 LOC)
src-tauri/src/services/video_processor.rs
    fn test_extract_frame_file_not_found() {
        let result = extract_frame("/nonexistent/video.mp4", 1.0);
        assert!(result.is_err());
    }
test_extract_frames_interval_calculation function · rust · L255-L260 (6 LOC)
src-tauri/src/services/video_processor.rs
    fn test_extract_frames_interval_calculation() {
        let duration = 10.0;
        let count = 5;
        let expected_interval = duration / count as f64;
        assert_eq!(expected_interval, 2.0);
    }
default function · rust · L47-L56 (10 LOC)
src-tauri/src/services/video_stitcher.rs
    fn default() -> Self {
        Self {
            output_path: "output.mp4".to_string(),
            target_resolution: None,
            target_fps: None,
            codec: "libx264".to_string(),
            crf: 23,
            normalize: false,
        }
    }
proxy function · rust · L61-L70 (10 LOC)
src-tauri/src/services/video_stitcher.rs
    pub fn proxy(output_path: String) -> Self {
        Self {
            output_path,
            target_resolution: Some((PROXY_WIDTH, PROXY_HEIGHT)),
            target_fps: Some(PROXY_FPS),
            codec: "libx264".to_string(),
            crf: 28, // Lower quality for proxy (smaller file)
            normalize: true,
        }
    }
normalize_video function · rust · L103-L141 (39 LOC)
src-tauri/src/services/video_stitcher.rs
pub fn normalize_video(input_path: &str, output_path: &str) -> Result<(), String> {
    if !Path::new(input_path).exists() {
        return Err(format!("Input video not found: {}", input_path));
    }

    // FFmpeg command to normalize video
    let mut cmd = Command::new("ffmpeg");
    cmd.arg("-i").arg(input_path);

    // Video: scale to 720p, set fps to 30
    cmd.arg("-vf").arg(format!("scale={}:{}", PROXY_WIDTH, PROXY_HEIGHT));
    cmd.arg("-r").arg(PROXY_FPS.to_string());

    // Video codec settings
    cmd.arg("-c:v").arg("libx264");
    cmd.arg("-preset").arg("fast");
    cmd.arg("-crf").arg("28"); // Lower quality for proxy

    // Audio: normalize to AAC
    cmd.arg("-c:a").arg("aac");
    cmd.arg("-b:a").arg("128k");
    cmd.arg("-ar").arg("48000"); // Standard sample rate

    // Fast start for web playback
    cmd.arg("-movflags").arg("faststart");

    cmd.arg(output_path);
    cmd.arg("-y"); // Overwrite

    let output = cmd.output()
        .map_err(|e| format!("Fai
Repobility analyzer · published findings · https://repobility.com
stitch_videos function · rust · L151-L217 (67 LOC)
src-tauri/src/services/video_stitcher.rs
pub fn stitch_videos(video_paths: &[String], config: &StitchConfig) -> Result<StitchResult, String> {
    if video_paths.is_empty() {
        return Err("No videos to stitch".to_string());
    }

    if video_paths.len() == 1 {
        // Single video - normalize (or copy based on config)
        if config.normalize {
            normalize_video(&video_paths[0], &config.output_path)?;
        } else {
            copy_video(&video_paths[0], &config.output_path)?;
        }
        return get_result(&config.output_path);
    }

    // If normalizing, first normalize all clips
    let paths_to_stitch = if config.normalize {
        let normalized_paths = normalize_all_videos(video_paths)?;
        normalized_paths
    } else {
        video_paths.to_vec()
    };

    // Create concat file for FFmpeg
    let concat_file = create_concat_file(&paths_to_stitch)?;

    // Build FFmpeg command for concat
    let mut cmd = Command::new("ffmpeg");
    cmd.arg("-f").arg("concat");
    cmd.arg("-s
normalize_all_videos function · rust · L220-L237 (18 LOC)
src-tauri/src/services/video_stitcher.rs
fn normalize_all_videos(video_paths: &[String]) -> Result<Vec<String>, String> {
    let temp_dir = std::env::temp_dir().join("video_editor_norm");
    fs::create_dir_all(&temp_dir)
        .map_err(|e| format!("Failed to create temp dir: {}", e))?;

    let mut normalized_paths = Vec::new();
    for (i, input_path) in video_paths.iter().enumerate() {
        let output_path = temp_dir.join(format!("normalized_{}.mp4", i));
        let output_str = output_path.to_str()
            .ok_or("Invalid temp path")?
            .to_string();

        normalize_video(input_path, &output_str)?;
        normalized_paths.push(output_str);
    }

    Ok(normalized_paths)
}
cleanup_normalized_videos function · rust · L240-L250 (11 LOC)
src-tauri/src/services/video_stitcher.rs
fn cleanup_normalized_videos(normalized_paths: &[String], original_paths: &[String]) {
    for path in normalized_paths {
        // Only delete if it's a temp file (not an original)
        if !original_paths.contains(path) {
            let _ = std::fs::remove_file(path);
        }
    }
    // Try to remove temp dir
    let temp_dir = std::env::temp_dir().join("video_editor_norm");
    let _ = fs::remove_dir(temp_dir);
}
‹ prevpage 7 / 9next ›