// 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_video" } fn from_formats(&self) -> Vec<&'static str> { vec!["mp4", "webm", "mkv", "avi", "mov", "wmv", "flv", "gif"] } fn to_formats(&self) -> Vec<&'static str> { vec!["mp4", "webm", "mkv", "avi", "mov", "wmv", "flv", "gif"] } fn familiarity(&self, _from: &str, to: &str) -> u8 { match to { "mp4" | "gif" => 255, "mov" => 240, "webm" => 220, "mkv" => 180, "wmv" => 160, "flv" => 120, "avi" => 100, _ => 128, } } fn quality(&self, _from: &str, to: &str) -> u8 { match to { "mkv" => 250, "mov" => 250, "mp4" => 240, "webm" => 210, "wmv" => 200, "flv" => 180, "avi" => 150, "gif" => 80, _ => 200, } } fn speed(&self, _from: &str, to: &str) -> u8 { match to { "mp4" => 220, "mov" => 220, "mkv" => 200, "flv" => 190, "avi" => 180, "wmv" => 170, "gif" => 150, "webm" => 100, _ => 150, } } 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 { "mp4" | "mkv" | "mov" => { raw_args.extend(vec![ "-c:v", "libx264", "-crf", "23", "-c:a", "aac", "-pix_fmt", "yuv420p", ]); } "webm" => { raw_args.extend(vec![ "-c:v", "libvpx-vp9", "-crf", "30", "-b:v", "0", "-c:a", "libopus", ]); } "avi" => { raw_args.extend(vec![ "-c:v", "mpeg4", "-vtag", "xvid", "-qscale:v", "3", "-c:a", "libmp3lame", ]); } "gif" => { raw_args.extend(vec!["-vf", "fps=15,scale=320:-1:flags=lanczos"]); } _ => { raw_args.extend(vec!["-qscale:v", "3"]); } } 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()) } }