File size: 2,437 Bytes
04a1f81 e7495da 1f2ab31 f05a886 04a1f81 0df918b 04a1f81 0df918b 04a1f81 fbb769b 04a1f81 fbb769b 04a1f81 fbb769b 7b12459 fbb769b 0df918b d330638 7b12459 fbb769b d330638 7b12459 d330638 0df918b 04a1f81 0df918b 04a1f81 fbb769b 04a1f81 d330638 fbb769b 65e7f7d fbb769b 04a1f81 fbb769b 04a1f81 d330638 fbb769b 0df918b fbb769b d330638 04a1f81 0df918b f05a886 7b12459 0df918b f05a886 e7495da |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
use pyo3::prelude::*;
use std::process::Command;
use std::path::Path;
#[pyfunction]
fn render_video(
id: usize,
image_path: String,
audio_path: String,
duration: f64,
words: Vec<(String, (u32, u32, u32, u32))>,
output_dir: String,
) -> PyResult<String> {
// Validate paths
if !Path::new(&image_path).exists() {
return Err(pyo3::exceptions::PyFileNotFoundError::new_err(format!(
"Image not found: {}",
image_path
)));
}
if !Path::new(&audio_path).exists() {
return Err(pyo3::exceptions::PyFileNotFoundError::new_err(format!(
"Audio not found: {}",
audio_path
)));
}
let output_path = format!("{}/clip{}.mp4", output_dir, id);
let duration_str = duration.to_string();
let n_words = words.len() as f64;
let highlight_duration = duration / n_words;
// Build filter graph
let mut filters = vec![];
for (i, (_, (x, y, w, h))) in words.iter().enumerate() {
let start = i as f64 * highlight_duration;
let end = start + highlight_duration;
filters.push(format!(
"drawbox=x={x}:y={y}:w={w}:h={h}:color=yellow@0.5:t=fill:enable='between(t,{start:.2},{end:.2})'"
));
}
let filter_chain = filters.join(",");
// FFmpeg command
let status = Command::new("ffmpeg")
.args(&[
"-y",
"-loop", "1",
"-i", &image_path,
"-i", &audio_path,
"-vf", &filter_chain,
"-c:v", "libx264",
"-preset", "fast",
"-crf", "18",
"-pix_fmt", "yuv420p",
"-movflags", "+faststart",
"-c:a", "aac",
"-b:a", "192k",
"-r", "60",
"-fps_mode", "vfr", // Replaces deprecated -vsync
"-t", &duration_str,
&output_path,
])
.status();
match status {
Ok(exit_status) if exit_status.success() => Ok(output_path),
Ok(_) => Err(pyo3::exceptions::PyRuntimeError::new_err(
"FFmpeg command failed. Check filter syntax.",
)),
Err(e) => Err(pyo3::exceptions::PyRuntimeError::new_err(format!(
"Failed to execute FFmpeg: {}",
e
))),
}
}
#[pymodule]
fn rust_highlight(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(render_video, m)?)?;
Ok(())
} |