1pub(crate) mod nodes;
2use std::fmt::Debug;
3
4use crate::nodes::filter::{Filter, FilterNode, FilterOptions};
5use crate::nodes::fnode_type::FNodeType;
6use crate::nodes::stream::Stream;
7use crate::nodes::stream::StreamType;
8pub use nodes::*;
9
10pub fn stringify(nodes: Vec<FNodeType>) -> String {
11 let mut str = "".to_string();
12 let inputs: Vec<_> = nodes
13 .clone()
14 .into_iter()
15 .filter(|node| match node.clone() {
16 FNodeType::Stream(node) => {
17 if let StreamType::Input = node.stream_type {
18 true
19 } else {
20 false
21 }
22 }
23 _ => false,
24 })
25 .collect();
26 inputs.clone().into_iter().for_each(|node| {
27 if let FNodeType::Stream(node) = node {
28 str = str.clone() + "-i " + node.path.as_str() + " ";
29 }
30 });
31
32 str = str + "-filter_complex '";
34 let filters: String = nodes
35 .clone()
36 .into_iter()
37 .filter(|node| match &node.clone() {
38 FNodeType::FilterNode(node) => true,
39 _ => false,
40 })
41 .map(|node| format_operation(Into::<FilterNode>::into(node.clone())))
42 .collect::<Vec<String>>()
43 .join(";");
44
45 let filtered_output: Vec<_> = nodes
46 .clone()
47 .into_iter()
48 .filter(|node| match node.clone() {
49 FNodeType::Stream(node) => {
50 if let StreamType::Output = node.stream_type {
51 true
52 } else {
53 false
54 }
55 }
56 _ => false,
57 })
58 .collect();
59
60 let mut maps = String::from("");
61 filtered_output.clone().into_iter().for_each(|node| {
62 let n: Stream = node.into();
63 if let Some(inputs) = n.inputs {
64 inputs
65 .into_iter()
66 .for_each(|input| maps = format!("{} -map '[{}]'", maps, input));
67 }
68 });
69
70 let output = filtered_output
71 .clone()
72 .into_iter()
73 .map(|node| Into::<Stream>::into(node).path)
74 .collect::<Vec<String>>()
75 .join("");
76
77 format!("{}{}' {} {}", str, filters, maps, output)
78}
79
80pub fn format_filter(filters: Vec<Filter>) -> String {
81 let filters: Vec<String> = filters
82 .into_iter()
83 .map(|filter| {
84 let mut filters = "".to_string();
85 match filter.options {
86 FilterOptions::HashMap(filter) => {
87 filters = filter
88 .into_iter()
89 .map(|option| return format!("{}={}", option.0, option.1))
90 .collect::<Vec<_>>()
91 .join(":")
92 }
93 FilterOptions::String(filter) => filters = filter,
94 }
95 format!("{}={}", filter.name, filters)
96 })
97 .collect();
98 filters.join(",")
99}
100
101pub fn format_operation(filterNode: FilterNode) -> String {
102 let ins = filterNode
103 .inputs
104 .into_iter()
105 .map(|input| format!("[{}]", input))
106 .collect::<Vec<String>>()
107 .join("");
108 let ops = format_filter(filterNode.filters);
109 let outs = filterNode
110 .outputs
111 .into_iter()
112 .map(|output| format!("[{}]", output))
113 .collect::<Vec<String>>()
114 .join("");
115 format!("{}{}{}", ins, ops, outs)
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121 use crate::nodes::filter::FilterOptions;
122 use crate::nodes::stream::StreamType;
123 use map_macro::hash_map;
124 use std::{path, vec};
125
126 #[test]
127 fn serialize() {
128 let output = Stream {
129 path: "/data/vid-modified.mp4".to_string(),
130 name: "input1".to_string(),
131 stream_type: StreamType::Output,
132 inputs: None,
133 };
134 println!("{:#?}", serde_json::to_string(&output).unwrap());
135 let overlay = FilterNode {
136 filters: vec![Filter {
137 name: "fade".to_string(),
138 options: FilterOptions::HashMap(hash_map! {
139 "type".to_string()=>"in".to_string(),
140 "st".to_string()=>"0".to_string(),
141 "duration".to_string()=>"1".to_string()
142 }),
143 }],
144
145 name: "overlay".to_string(),
146 inputs: vec!["0:v".to_string()],
147 outputs: vec![],
148 };
149 }
151
152 #[test]
153 fn it_works() {
154 let output = Stream {
155 path: "./data/vid-modified.mp4".to_string(),
156 name: "input1".to_string(),
157 stream_type: StreamType::Output,
158 inputs: Some(vec!["audio".to_string(), "out0".to_string()]),
159 };
160 let input = Stream {
161 path: "./data/vid.mp4".to_string(),
162 name: "input2".to_string(),
163 stream_type: StreamType::Input,
164 inputs: None,
165 };
166 let sound_input: Stream = Stream {
167 path: "./data/sound.mp3".to_string(),
168 name: "sound_input".to_string(),
169 stream_type: StreamType::Input,
170 inputs: None,
171 };
172 let trim = FilterNode {
173 filters: vec![Filter {
174 name: "trim".to_string(),
175 options: FilterOptions::HashMap(hash_map! {
176 "duration".to_string()=>"4".to_string(),
177 }),
178 }],
179
180 name: "trim".to_string(),
181 inputs: vec!["0:v".to_string()],
182 outputs: vec!["out0".to_string()],
183 };
184
185 let audio = FilterNode {
186 filters: vec![Filter {
187 name: "volume".to_string(),
188 options: FilterOptions::String("1".to_string()),
189 }],
190 name: "volume".to_string(),
191 outputs: vec!["audio".to_string()],
192 inputs: vec!["trimmed_audio".to_string()],
193 };
194
195 let audio_trim = FilterNode {
196 filters: vec![Filter {
197 name: "atrim".to_string(),
198 options: FilterOptions::HashMap(hash_map! {
199 "duration".to_string()=>"4".to_string()
200 }),
201 }],
202 name: "atrim".to_string(),
203 inputs: vec!["1:a".to_string()],
204 outputs: vec!["trimmed_audio".to_string()],
205 };
206
207 let string = stringify(vec![
208 FNodeType::Stream(input),
209 FNodeType::Stream(sound_input),
210 FNodeType::FilterNode(trim),
211 FNodeType::FilterNode(audio_trim),
212 FNodeType::FilterNode(audio),
213 FNodeType::Stream(output),
214 ]);
215 println!("{:?}", string);
216 assert_eq!(string, "-i ./data/vid.mp4 -i ./data/sound.mp3 -filter_complex '[0:v]trim=duration=4[out0];[1:a]atrim=duration=4[trimmed_audio];[trimmed_audio]volume=1[audio]' -map '[audio]' -map '[out0]' ./data/vid-modified.mp4".to_string());
217 }
218}