1use crate::task::{Task, TaskPriority, TaskType};
4use crate::workflow::Workflow;
5use std::collections::HashMap;
6use std::path::PathBuf;
7use std::time::Duration;
8
9#[allow(dead_code)]
13#[must_use]
14pub fn watch_folder_transcode(
15 input_folder: PathBuf,
16 output_folder: PathBuf,
17 preset: String,
18) -> Workflow {
19 let mut workflow = Workflow::new("watch-folder-transcode")
20 .with_description("Auto-transcode files from watch folder");
21
22 let transcode_task = Task::new(
24 "transcode",
25 TaskType::Transcode {
26 input: input_folder.join("input.mp4"),
27 output: output_folder.join("output.mp4"),
28 preset,
29 params: HashMap::new(),
30 },
31 )
32 .with_priority(TaskPriority::High);
33
34 workflow.add_task(transcode_task);
35 workflow
36}
37
38#[allow(dead_code)]
42#[must_use]
43pub fn multi_pass_encoding(
44 source: PathBuf,
45 proxy_output: PathBuf,
46 final_output: PathBuf,
47 qc_profile: String,
48) -> Workflow {
49 let mut workflow = Workflow::new("multi-pass-encoding")
50 .with_description("Multi-pass encoding with QC validation");
51
52 let proxy_task = Task::new(
54 "create-proxy",
55 TaskType::Transcode {
56 input: source.clone(),
57 output: proxy_output.clone(),
58 preset: "proxy-lowres".to_string(),
59 params: HashMap::new(),
60 },
61 )
62 .with_priority(TaskPriority::High);
63
64 let proxy_id = workflow.add_task(proxy_task);
65
66 let qc_task = Task::new(
68 "qc-validation",
69 TaskType::QualityControl {
70 input: proxy_output.clone(),
71 profile: qc_profile,
72 rules: vec!["video_quality".to_string(), "audio_levels".to_string()],
73 },
74 );
75
76 let qc_id = workflow.add_task(qc_task);
77 workflow.add_edge(proxy_id, qc_id).unwrap_or(());
78
79 let final_task = Task::new(
81 "final-encode",
82 TaskType::Transcode {
83 input: source,
84 output: final_output,
85 preset: "high-quality".to_string(),
86 params: HashMap::new(),
87 },
88 )
89 .with_priority(TaskPriority::Critical)
90 .with_timeout(Duration::from_secs(7200)); let final_id = workflow.add_task(final_task);
93 workflow.add_edge(qc_id, final_id).unwrap_or(());
94
95 workflow
96}
97
98#[allow(dead_code)]
102#[must_use]
103pub fn validation_pipeline(
104 input_file: PathBuf,
105 archive_destination: String,
106 notification_email: Vec<String>,
107) -> Workflow {
108 let mut workflow = Workflow::new("validation-pipeline")
109 .with_description("Complete validation and archival pipeline");
110
111 let validation_task = Task::new(
113 "format-validation",
114 TaskType::Analysis {
115 input: input_file.clone(),
116 analyses: vec![
117 crate::task::AnalysisType::VideoQuality,
118 crate::task::AnalysisType::AudioLevels,
119 ],
120 output: None,
121 },
122 );
123
124 let validation_id = workflow.add_task(validation_task);
125
126 let qc_task = Task::new(
128 "quality-check",
129 TaskType::QualityControl {
130 input: input_file.clone(),
131 profile: "broadcast".to_string(),
132 rules: vec![
133 "video_bitrate".to_string(),
134 "audio_channels".to_string(),
135 "format_compliance".to_string(),
136 ],
137 },
138 );
139
140 let qc_id = workflow.add_task(qc_task);
141 workflow.add_edge(validation_id, qc_id).unwrap_or(());
142
143 let archive_task = Task::new(
145 "archive",
146 TaskType::Transfer {
147 source: input_file.to_string_lossy().to_string(),
148 destination: archive_destination,
149 protocol: crate::task::TransferProtocol::S3,
150 options: HashMap::new(),
151 },
152 )
153 .with_timeout(Duration::from_secs(3600));
154
155 let archive_id = workflow.add_task(archive_task);
156 workflow.add_edge(qc_id, archive_id).unwrap_or(());
157
158 let notify_task = Task::new(
160 "notify-completion",
161 TaskType::Notification {
162 channel: crate::task::NotificationChannel::Email {
163 to: notification_email,
164 subject: "Validation Pipeline Completed".to_string(),
165 },
166 message: "File successfully validated and archived".to_string(),
167 metadata: HashMap::new(),
168 },
169 );
170
171 let notify_id = workflow.add_task(notify_task);
172 workflow.add_edge(archive_id, notify_id).unwrap_or(());
173
174 workflow
175}
176
177#[allow(dead_code)]
181#[must_use]
182pub fn distribution_workflow(
183 source: PathBuf,
184 output_dir: PathBuf,
185 upload_destinations: Vec<String>,
186) -> Workflow {
187 let mut workflow = Workflow::new("distribution-workflow")
188 .with_description("Multi-format distribution pipeline");
189
190 let source_task = Task::new(
191 "source-analysis",
192 TaskType::Analysis {
193 input: source.clone(),
194 analyses: vec![crate::task::AnalysisType::VideoQuality],
195 output: None,
196 },
197 );
198
199 let source_id = workflow.add_task(source_task);
200
201 let formats = vec![
203 ("web-hd", "1080p-web"),
204 ("web-sd", "720p-web"),
205 ("mobile", "480p-mobile"),
206 ("broadcast", "broadcast-hd"),
207 ];
208
209 let mut encode_ids = Vec::new();
210
211 for (name, preset) in formats {
212 let encode_task = Task::new(
213 format!("encode-{name}"),
214 TaskType::Transcode {
215 input: source.clone(),
216 output: output_dir.join(format!("{name}.mp4")),
217 preset: preset.to_string(),
218 params: HashMap::new(),
219 },
220 )
221 .with_priority(TaskPriority::High);
222
223 let encode_id = workflow.add_task(encode_task);
224 workflow.add_edge(source_id, encode_id).unwrap_or(());
225 encode_ids.push(encode_id);
226 }
227
228 for (idx, destination) in upload_destinations.iter().enumerate() {
230 let upload_task = Task::new(
231 format!("upload-{idx}"),
232 TaskType::Transfer {
233 source: output_dir.to_string_lossy().to_string(),
234 destination: destination.clone(),
235 protocol: crate::task::TransferProtocol::S3,
236 options: HashMap::new(),
237 },
238 );
239
240 let upload_id = workflow.add_task(upload_task);
241
242 for &encode_id in &encode_ids {
244 workflow.add_edge(encode_id, upload_id).unwrap_or(());
245 }
246 }
247
248 workflow
249}
250
251#[allow(dead_code)]
255#[must_use]
256pub fn archive_workflow(
257 source: PathBuf,
258 proxy_dir: PathBuf,
259 archive_destination: String,
260) -> Workflow {
261 let mut workflow = Workflow::new("archive-workflow")
262 .with_description("Complete archival workflow with proxies");
263
264 let ingest_task = Task::new(
266 "ingest-validate",
267 TaskType::Analysis {
268 input: source.clone(),
269 analyses: vec![
270 crate::task::AnalysisType::VideoQuality,
271 crate::task::AnalysisType::AudioLevels,
272 crate::task::AnalysisType::Color,
273 ],
274 output: Some(proxy_dir.join("analysis.json")),
275 },
276 );
277
278 let ingest_id = workflow.add_task(ingest_task);
279
280 let proxy_formats = vec![
282 ("proxy-high", "proxy-1080p"),
283 ("proxy-medium", "proxy-720p"),
284 ("proxy-low", "proxy-480p"),
285 ("thumbnail", "thumbnail-grid"),
286 ];
287
288 let mut proxy_ids = Vec::new();
289
290 for (name, preset) in proxy_formats {
291 let proxy_task = Task::new(
292 format!("create-{name}"),
293 TaskType::Transcode {
294 input: source.clone(),
295 output: proxy_dir.join(format!("{name}.mp4")),
296 preset: preset.to_string(),
297 params: HashMap::new(),
298 },
299 );
300
301 let proxy_id = workflow.add_task(proxy_task);
302 workflow.add_edge(ingest_id, proxy_id).unwrap_or(());
303 proxy_ids.push(proxy_id);
304 }
305
306 let archive_task = Task::new(
308 "deep-archive",
309 TaskType::Transfer {
310 source: source.to_string_lossy().to_string(),
311 destination: archive_destination,
312 protocol: crate::task::TransferProtocol::S3,
313 options: {
314 let mut opts = HashMap::new();
315 opts.insert("storage_class".to_string(), "GLACIER".to_string());
316 opts
317 },
318 },
319 )
320 .with_timeout(Duration::from_secs(7200));
321
322 let archive_id = workflow.add_task(archive_task);
323
324 for proxy_id in proxy_ids {
326 workflow.add_edge(proxy_id, archive_id).unwrap_or(());
327 }
328
329 workflow
330}
331
332#[allow(dead_code)]
336#[must_use]
337pub fn parallel_processing(inputs: Vec<PathBuf>, output_dir: PathBuf, preset: String) -> Workflow {
338 let mut workflow =
339 Workflow::new("parallel-processing").with_description("Process multiple files in parallel");
340
341 for (idx, input) in inputs.iter().enumerate() {
342 let task = Task::new(
343 format!("process-{idx}"),
344 TaskType::Transcode {
345 input: input.clone(),
346 output: output_dir.join(format!("output_{idx}.mp4")),
347 preset: preset.clone(),
348 params: HashMap::new(),
349 },
350 )
351 .with_priority(TaskPriority::Normal);
352
353 workflow.add_task(task);
354 }
355
356 workflow
357}
358
359#[allow(dead_code)]
363#[must_use]
364pub fn sequential_processing(input: PathBuf, output_dir: PathBuf, stages: Vec<String>) -> Workflow {
365 let mut workflow = Workflow::new("sequential-processing")
366 .with_description("Sequential multi-stage processing");
367
368 let mut previous_id = None;
369 let mut current_input = input;
370
371 for (idx, stage) in stages.iter().enumerate() {
372 let output = output_dir.join(format!("stage_{idx}.mp4"));
373
374 let task = Task::new(
375 format!("stage-{idx}"),
376 TaskType::Transcode {
377 input: current_input.clone(),
378 output: output.clone(),
379 preset: stage.clone(),
380 params: HashMap::new(),
381 },
382 );
383
384 let task_id = workflow.add_task(task);
385
386 if let Some(prev_id) = previous_id {
387 workflow.add_edge(prev_id, task_id).unwrap_or(());
388 }
389
390 previous_id = Some(task_id);
391 current_input = output;
392 }
393
394 workflow
395}
396
397#[allow(dead_code)]
401#[must_use]
402pub fn conditional_workflow(input: PathBuf, output_hq: PathBuf, output_lq: PathBuf) -> Workflow {
403 let mut workflow = Workflow::new("conditional-workflow")
404 .with_description("Conditional processing based on input properties");
405
406 let analysis_task = Task::new(
408 "analyze-input",
409 TaskType::Analysis {
410 input: input.clone(),
411 analyses: vec![crate::task::AnalysisType::VideoQuality],
412 output: None,
413 },
414 );
415
416 let analysis_id = workflow.add_task(analysis_task);
417
418 let hq_task = Task::new(
420 "high-quality-encode",
421 TaskType::Transcode {
422 input: input.clone(),
423 output: output_hq,
424 preset: "ultra-hq".to_string(),
425 params: HashMap::new(),
426 },
427 )
428 .with_condition("input.bitrate > 10000000"); let hq_id = workflow.add_task(hq_task);
431 workflow.add_edge(analysis_id, hq_id).unwrap_or(());
432
433 let lq_task = Task::new(
435 "standard-quality-encode",
436 TaskType::Transcode {
437 input,
438 output: output_lq,
439 preset: "standard".to_string(),
440 params: HashMap::new(),
441 },
442 )
443 .with_condition("input.bitrate <= 10000000");
444
445 let lq_id = workflow.add_task(lq_task);
446 workflow.add_edge(analysis_id, lq_id).unwrap_or(());
447
448 workflow
449}
450
451#[cfg(test)]
452mod tests {
453 use super::*;
454
455 #[test]
456 fn test_watch_folder_transcode() {
457 let workflow = watch_folder_transcode(
458 PathBuf::from("/input"),
459 PathBuf::from("/output"),
460 "h264".to_string(),
461 );
462
463 assert_eq!(workflow.name, "watch-folder-transcode");
464 assert_eq!(workflow.tasks.len(), 1);
465 }
466
467 #[test]
468 fn test_multi_pass_encoding() {
469 let workflow = multi_pass_encoding(
470 PathBuf::from("/source.mp4"),
471 PathBuf::from("/proxy.mp4"),
472 PathBuf::from("/final.mp4"),
473 "broadcast".to_string(),
474 );
475
476 assert_eq!(workflow.name, "multi-pass-encoding");
477 assert_eq!(workflow.tasks.len(), 3);
478 assert_eq!(workflow.edges.len(), 2);
479 }
480
481 #[test]
482 fn test_validation_pipeline() {
483 let workflow = validation_pipeline(
484 PathBuf::from("/input.mp4"),
485 "s3://bucket/archive".to_string(),
486 vec!["admin@example.com".to_string()],
487 );
488
489 assert_eq!(workflow.name, "validation-pipeline");
490 assert_eq!(workflow.tasks.len(), 4);
491 assert!(workflow.validate().is_ok());
492 }
493
494 #[test]
495 fn test_distribution_workflow() {
496 let workflow = distribution_workflow(
497 PathBuf::from("/source.mp4"),
498 PathBuf::from("/output"),
499 vec!["s3://cdn1".to_string(), "s3://cdn2".to_string()],
500 );
501
502 assert_eq!(workflow.name, "distribution-workflow");
503 assert!(workflow.tasks.len() >= 5); }
505
506 #[test]
507 fn test_archive_workflow() {
508 let workflow = archive_workflow(
509 PathBuf::from("/source.mp4"),
510 PathBuf::from("/proxies"),
511 "s3://archive".to_string(),
512 );
513
514 assert_eq!(workflow.name, "archive-workflow");
515 assert!(workflow.tasks.len() >= 5); }
517
518 #[test]
519 fn test_parallel_processing() {
520 let inputs = vec![
521 PathBuf::from("/input1.mp4"),
522 PathBuf::from("/input2.mp4"),
523 PathBuf::from("/input3.mp4"),
524 ];
525
526 let workflow = parallel_processing(inputs, PathBuf::from("/output"), "h264".to_string());
527
528 assert_eq!(workflow.tasks.len(), 3);
529 assert_eq!(workflow.edges.len(), 0); }
531
532 #[test]
533 fn test_sequential_processing() {
534 let stages = vec![
535 "denoise".to_string(),
536 "color-correct".to_string(),
537 "stabilize".to_string(),
538 ];
539
540 let workflow = sequential_processing(
541 PathBuf::from("/input.mp4"),
542 PathBuf::from("/output"),
543 stages,
544 );
545
546 assert_eq!(workflow.tasks.len(), 3);
547 assert_eq!(workflow.edges.len(), 2); }
549
550 #[test]
551 fn test_conditional_workflow() {
552 let workflow = conditional_workflow(
553 PathBuf::from("/input.mp4"),
554 PathBuf::from("/output_hq.mp4"),
555 PathBuf::from("/output_lq.mp4"),
556 );
557
558 assert_eq!(workflow.tasks.len(), 3); assert!(workflow.validate().is_ok());
560 }
561}