sreepathi-ravikumar's picture
Update rust_highlight/src/lib.rs
65e7f7d verified
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(())
}