feat: add ffmpeg plugin and update plugin interface to support temporary directories and context-dependent metrics
Release / check-release (push) Successful in 15s
Release / build_gnu (push) Skipped
Release / build_musl (push) Skipped
Release / build_windows (push) Skipped
Release / package_gnu (deb) (push) Skipped
Release / package_gnu (rpm) (push) Skipped
Release / package_musl (deb) (push) Skipped
Release / package_musl (rpm) (push) Skipped
Release / publish-release (push) Skipped
CI / Test (push) Successful in 1m31s
CI / Build Linux (push) Successful in 1m47s
Release / check-release (push) Successful in 15s
Release / build_gnu (push) Skipped
Release / build_musl (push) Skipped
Release / build_windows (push) Skipped
Release / package_gnu (deb) (push) Skipped
Release / package_gnu (rpm) (push) Skipped
Release / package_musl (deb) (push) Skipped
Release / package_musl (rpm) (push) Skipped
Release / publish-release (push) Skipped
CI / Test (push) Successful in 1m31s
CI / Build Linux (push) Successful in 1m47s
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
// Copyright (C) 2026 Elias Wendland <eliaswendland@pm.me>
|
||||
//
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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<Vec<u8>, 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())
|
||||
}
|
||||
}
|
||||
+41
-8
@@ -1,18 +1,51 @@
|
||||
// Copyright (C) 2026 Elias Wendland <eliaswendland@pm.me>
|
||||
//
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::plugin::Plugin;
|
||||
use image::ImageFormat;
|
||||
use std::io::Cursor;
|
||||
use std::path::Path;
|
||||
|
||||
pub struct PluginImpl;
|
||||
|
||||
impl Plugin for PluginImpl {
|
||||
fn name(&self) -> &'static str { "jpeg_to_png" }
|
||||
fn from_formats(&self) -> Vec<&'static str> { vec!["jpeg"] }
|
||||
fn to_formats(&self) -> Vec<&'static str> { vec!["png"] }
|
||||
fn familiarity(&self) -> u8 { 255 }
|
||||
fn quality(&self) -> u8 { 255 }
|
||||
fn speed(&self) -> u8 { 200 }
|
||||
|
||||
fn convert(&self, input: &[u8], _from: &str, _to: &str) -> Result<Vec<u8>, String> {
|
||||
fn name(&self) -> &'static str {
|
||||
"jpeg_to_png"
|
||||
}
|
||||
fn from_formats(&self) -> Vec<&'static str> {
|
||||
vec!["jpeg"]
|
||||
}
|
||||
fn to_formats(&self) -> Vec<&'static str> {
|
||||
vec!["png"]
|
||||
}
|
||||
fn familiarity(&self, _from: &str, _to: &str) -> u8 {
|
||||
255
|
||||
}
|
||||
fn quality(&self, _from: &str, _to: &str) -> u8 {
|
||||
255
|
||||
}
|
||||
fn speed(&self, _from: &str, _to: &str) -> u8 {
|
||||
200
|
||||
}
|
||||
|
||||
fn convert(
|
||||
&self,
|
||||
input: &[u8],
|
||||
_from: &str,
|
||||
_to: &str,
|
||||
_temp_dir: &Path,
|
||||
) -> Result<Vec<u8>, String> {
|
||||
let img = image::load_from_memory_with_format(input, ImageFormat::Jpeg)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let mut buf = Vec::new();
|
||||
|
||||
+41
-8
@@ -1,18 +1,51 @@
|
||||
// Copyright (C) 2026 Elias Wendland <eliaswendland@pm.me>
|
||||
//
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::plugin::Plugin;
|
||||
use image::ImageFormat;
|
||||
use std::io::Cursor;
|
||||
use std::path::Path;
|
||||
|
||||
pub struct PluginImpl;
|
||||
|
||||
impl Plugin for PluginImpl {
|
||||
fn name(&self) -> &'static str { "png_to_jpeg" }
|
||||
fn from_formats(&self) -> Vec<&'static str> { vec!["png"] }
|
||||
fn to_formats(&self) -> Vec<&'static str> { vec!["jpeg"] }
|
||||
fn familiarity(&self) -> u8 { 255 }
|
||||
fn quality(&self) -> u8 { 200 } // JPEG has some compression loss
|
||||
fn speed(&self) -> u8 { 220 }
|
||||
|
||||
fn convert(&self, input: &[u8], _from: &str, _to: &str) -> Result<Vec<u8>, String> {
|
||||
fn name(&self) -> &'static str {
|
||||
"png_to_jpeg"
|
||||
}
|
||||
fn from_formats(&self) -> Vec<&'static str> {
|
||||
vec!["png"]
|
||||
}
|
||||
fn to_formats(&self) -> Vec<&'static str> {
|
||||
vec!["jpeg"]
|
||||
}
|
||||
fn familiarity(&self, _from: &str, _to: &str) -> u8 {
|
||||
255
|
||||
}
|
||||
fn quality(&self, _from: &str, _to: &str) -> u8 {
|
||||
200
|
||||
} // JPEG has some compression loss
|
||||
fn speed(&self, _from: &str, _to: &str) -> u8 {
|
||||
220
|
||||
}
|
||||
|
||||
fn convert(
|
||||
&self,
|
||||
input: &[u8],
|
||||
_from: &str,
|
||||
_to: &str,
|
||||
_temp_dir: &Path,
|
||||
) -> Result<Vec<u8>, String> {
|
||||
let img = image::load_from_memory_with_format(input, ImageFormat::Png)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let mut buf = Vec::new();
|
||||
|
||||
Reference in New Issue
Block a user