// Copyright (C) 2026 Elias Wendland // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 3 exclusively. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . use crate::plugin::Plugin; use rust_ffmpeg::FFmpegBuilder; use std::io::Write; use std::path::Path; pub struct PluginImpl; impl Plugin for PluginImpl { fn name(&self) -> &'static str { "ffmpeg_audio" } fn from_formats(&self) -> Vec<&'static str> { vec![ "mp3", "wav", "ogg", "flac", "aac", "m4a", "opus", "wma", "amr", "aiff", "aiff", "au", ] } fn to_formats(&self) -> Vec<&'static str> { vec![ "mp3", "wav", "ogg", "flac", "aac", "m4a", "opus", "wma", "amr", "aiff", "aiff", "au", ] } fn familiarity(&self, _from: &str, to: &str) -> u8 { match to { "mp3" | "wav" => 255, "flac" | "m4a" | "aac" => 240, "ogg" | "opus" => 220, "wma" | "amr" => 150, "aiff" | "au" => 120, _ => 128, } } fn quality(&self, _from: &str, to: &str) -> u8 { match to { "flac" | "wav" | "aiff" | "au" => 255, "opus" | "ogg" | "aac" | "m4a" => 220, "mp3" => 200, "wma" | "amr" => 150, _ => 128, } } fn speed(&self, _from: &str, to: &str) -> u8 { match to { "wav" | "aiff" | "au" => 255, "flac" => 200, "mp3" | "aac" | "m4a" | "ogg" => 180, "opus" | "wma" | "amr" => 150, _ => 128, } } fn convert( &self, input: &[u8], _from: &str, to: &str, temp_dir: &Path, ) -> Result, String> { let mut temp_in = tempfile::Builder::new() .suffix(&format!(".{}", _from)) .tempfile_in(temp_dir) .map_err(|e| e.to_string())?; temp_in.write_all(input).map_err(|e| e.to_string())?; let temp_out = tempfile::Builder::new() .suffix(&format!(".{}", to)) .tempfile_in(temp_dir) .map_err(|e| e.to_string())?; let temp_out_path = temp_out.into_temp_path(); let in_path = temp_in.path().to_path_buf(); let mut raw_args = vec![]; match to { "mp3" => { raw_args.extend(vec!["-c:a", "libmp3lame", "-q:a", "2"]); } "ogg" => { raw_args.extend(vec!["-c:a", "libvorbis", "-q:a", "4"]); } "flac" => { raw_args.extend(vec!["-c:a", "flac"]); } "aac" | "m4a" => { raw_args.extend(vec!["-c:a", "aac", "-b:a", "192k"]); } "opus" => { raw_args.extend(vec!["-c:a", "libopus", "-b:a", "128k"]); } "wma" => { raw_args.extend(vec!["-c:a", "wmav2", "-b:a", "192k"]); } "amr" => { raw_args.extend(vec!["-ar", "8000", "-c:a", "libopencore_amrnb", "-b:a", "12.2k"]); } "wav" | "aiff" | "au" => { raw_args.extend(vec!["-c:a", "pcm_s16le"]); } _ => { raw_args.extend(vec!["-c:a", "copy"]); } } let rt = tokio::runtime::Runtime::new().map_err(|e| e.to_string())?; rt.block_on(async { FFmpegBuilder::new() .map_err(|e| e.to_string())? .input_path(in_path) .output_path(temp_out_path.to_path_buf()) .raw_args(raw_args) .overwrite() .run() .await .map_err(|e| e.to_string()) })?; std::fs::read(&temp_out_path).map_err(|e| e.to_string()) } }