nexus_common/media/processors/
video.rs1use async_trait::async_trait;
2use tokio::process::Command;
3
4use crate::{media::FileVariant, models::file::FileDetails, types::DynError};
5
6use super::{BaseProcessingOptions, VariantProcessor};
7
8pub struct VideoOptions {
9 width: String,
10 format: String,
11 content_type: String,
12}
13
14impl BaseProcessingOptions for VideoOptions {
15 fn content_type(&self) -> String {
16 self.content_type.clone()
17 }
18}
19
20pub struct VideoProcessor;
23
24#[async_trait]
25impl VariantProcessor for VideoProcessor {
26 type ProcessingOptions = VideoOptions;
27
28 fn get_valid_variants_for_content_type(_content_type: &str) -> Vec<FileVariant> {
29 vec![FileVariant::Main]
30 }
31
32 fn get_content_type_for_variant(_file: &FileDetails, _variant: &FileVariant) -> String {
33 String::from("video/mp4")
34 }
35
36 fn get_options_for_variant(
37 _file: &FileDetails,
38 _variant: &FileVariant,
39 ) -> Result<VideoOptions, DynError> {
40 Err("Not implemented".into())
43 }
44
45 async fn process(
46 origin_file_path: &str,
47 output_file_path: &str,
48 options: &VideoOptions,
49 ) -> Result<String, DynError> {
50 let origin_file_format = VideoProcessor::get_format(origin_file_path).await?;
51
52 let output = match origin_file_format == options.format {
53 true => output_file_path.to_string(),
54 false => format!("{}.{}", output_file_path, options.format),
55 };
56
57 let child_output = match Command::new("ffmpeg")
58 .arg("-i")
59 .arg(origin_file_path)
60 .arg("-vf")
61 .arg(format!("scale={}:-1", options.width))
62 .arg("-c:a")
63 .arg("copy")
64 .arg(output)
65 .output() .await
67 {
68 Ok(output) => output,
69 Err(err) => return Err(err.into()),
70 };
71
72 if child_output.status.success() {
73 Ok(String::from_utf8_lossy(&child_output.stdout).to_string())
74 } else {
75 Err(format!(
76 "FFmpeg command failed: {}",
77 String::from_utf8_lossy(&child_output.stderr)
78 )
79 .into())
80 }
81 }
82}
83
84impl VideoProcessor {
85 async fn get_format(input: &str) -> Result<String, DynError> {
87 let child_output = Command::new("ffmpeg")
88 .arg("-i")
89 .arg(input)
90 .arg("-f")
91 .arg("null")
92 .output() .await?;
94
95 if child_output.status.success() {
96 Ok(String::from_utf8_lossy(&child_output.stdout).to_string())
97 } else {
98 Err(format!(
99 "FFmpeg metadata extraction failed: {}",
100 String::from_utf8_lossy(&child_output.stderr)
101 )
102 .into())
103 }
104 }
105}