|
|
use pyo3::prelude::*; |
|
|
use pyo3::wrap_pyfunction; |
|
|
use std::fs::File; |
|
|
use std::io::Write; |
|
|
use std::process::Command; |
|
|
use uuid::Uuid; |
|
|
|
|
|
#[pyfunction] |
|
|
fn combine_clips(clips: Vec<String>) -> PyResult<String> { |
|
|
let paths: Vec<&str> = clips.iter().map(|s| s.as_str()).collect(); |
|
|
concat_videos(paths) |
|
|
|
|
|
} |
|
|
|
|
|
fn concat_videos(clips: Vec<&str>) -> PyResult<String> { |
|
|
|
|
|
let list_file = format!("/tmp/clips_{}.txt", Uuid::new_v4()); |
|
|
let mut file = File::create(&list_file) |
|
|
.map_err(|e| pyo3::exceptions::PyIOError::new_err(e.to_string()))?; |
|
|
for clip in &clips { writeln!(file, "file '{}'", clip) |
|
|
.map_err(|e| pyo3::exceptions::PyIOError::new_err(e.to_string()))?; |
|
|
} |
|
|
|
|
|
|
|
|
let output_file = format!("/tmp/final_video_{}.mp4", Uuid::new_v4()); |
|
|
let status = Command::new("ffmpeg") |
|
|
.args(&[ |
|
|
"-y", |
|
|
"-f", "concat", |
|
|
"-safe", "0", |
|
|
"-i", &list_file, |
|
|
"-c", "copy", |
|
|
&output_file, |
|
|
]) |
|
|
.status() |
|
|
.map_err(|e| pyo3::exceptions::PyRuntimeError::new_err(e.to_string()))?; |
|
|
|
|
|
if status.success() { |
|
|
Ok(output_file) |
|
|
} else { |
|
|
Err(pyo3::exceptions::PyRuntimeError::new_err("FFmpeg command failed")) |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
#[pymodule] |
|
|
fn rust_combiner(_py: Python, m: &PyModule) -> PyResult<()> { |
|
|
m.add_function(wrap_pyfunction!(combine_clips, m)?)?; |
|
|
Ok(()) |
|
|
} |