oxihuman_export/
resolve_export.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum ResolveTimelineType {
10 Edit,
11 Color,
12 Fusion,
13 Deliver,
14}
15
16#[derive(Debug, Clone)]
18pub struct ResolveClip {
19 pub name: String,
20 pub start_frame: i32,
21 pub end_frame: i32,
22 pub reel: String,
23}
24
25#[derive(Debug, Clone)]
27pub struct ResolveTimeline {
28 pub name: String,
29 pub timeline_type: ResolveTimelineType,
30 pub fps: f32,
31 pub clips: Vec<ResolveClip>,
32}
33
34pub fn new_resolve_timeline(name: &str, fps: f32, ttype: ResolveTimelineType) -> ResolveTimeline {
36 ResolveTimeline {
37 name: name.to_string(),
38 timeline_type: ttype,
39 fps,
40 clips: Vec::new(),
41 }
42}
43
44pub fn resolve_add_clip(tl: &mut ResolveTimeline, name: &str, start: i32, end: i32, reel: &str) {
46 tl.clips.push(ResolveClip {
47 name: name.to_string(),
48 start_frame: start,
49 end_frame: end,
50 reel: reel.to_string(),
51 });
52}
53
54pub fn resolve_clip_count(tl: &ResolveTimeline) -> usize {
56 tl.clips.len()
57}
58
59pub fn resolve_duration_frames(tl: &ResolveTimeline) -> i32 {
61 tl.clips.iter().map(|c| c.end_frame).max().unwrap_or(0)
62}
63
64pub fn validate_resolve_timeline(tl: &ResolveTimeline) -> bool {
66 tl.fps > 0.0 && tl.clips.iter().all(|c| c.end_frame > c.start_frame)
67}
68
69pub fn resolve_to_python(tl: &ResolveTimeline) -> String {
71 let mut out =
72 String::from("import DaVinciResolveScript as dvr\nresolve = dvr.scriptapp('Resolve')\n");
73 out.push_str("proj = resolve.GetProjectManager().GetCurrentProject()\n");
74 out.push_str(&format!("timeline = proj.CreateTimeline('{}')\n", tl.name));
75 for clip in &tl.clips {
76 out.push_str(&format!(
77 "timeline.AddClip('{}', {}, {})\n",
78 clip.name, clip.start_frame, clip.end_frame
79 ));
80 }
81 out
82}
83
84pub fn resolve_script_size(tl: &ResolveTimeline) -> usize {
86 resolve_to_python(tl).len()
87}
88
89pub fn resolve_clips_for_reel<'a>(tl: &'a ResolveTimeline, reel: &str) -> Vec<&'a ResolveClip> {
91 tl.clips.iter().filter(|c| c.reel == reel).collect()
92}
93
94pub fn timeline_type_name(tl: &ResolveTimeline) -> &'static str {
96 match tl.timeline_type {
97 ResolveTimelineType::Edit => "Edit",
98 ResolveTimelineType::Color => "Color",
99 ResolveTimelineType::Fusion => "Fusion",
100 ResolveTimelineType::Deliver => "Deliver",
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 fn sample_tl() -> ResolveTimeline {
109 let mut tl = new_resolve_timeline("MyEdit", 24.0, ResolveTimelineType::Edit);
110 resolve_add_clip(&mut tl, "shot_010", 1, 100, "A001");
111 resolve_add_clip(&mut tl, "shot_020", 101, 200, "A001");
112 tl
113 }
114
115 #[test]
116 fn test_clip_count() {
117 assert_eq!(resolve_clip_count(&sample_tl()), 2);
118 }
119
120 #[test]
121 fn test_duration() {
122 assert_eq!(resolve_duration_frames(&sample_tl()), 200);
123 }
124
125 #[test]
126 fn test_validate_valid() {
127 assert!(validate_resolve_timeline(&sample_tl()));
128 }
129
130 #[test]
131 fn test_validate_zero_fps() {
132 let tl = new_resolve_timeline("bad", 0.0, ResolveTimelineType::Edit);
133 assert!(!validate_resolve_timeline(&tl));
134 }
135
136 #[test]
137 fn test_to_python() {
138 let tl = sample_tl();
139 assert!(resolve_to_python(&tl).contains("shot_010"));
140 }
141
142 #[test]
143 fn test_clips_for_reel() {
144 let tl = sample_tl();
145 let reel_clips = resolve_clips_for_reel(&tl, "A001");
146 assert_eq!(reel_clips.len(), 2);
147 }
148
149 #[test]
150 fn test_timeline_type_name() {
151 let tl = sample_tl();
152 assert_eq!(timeline_type_name(&tl), "Edit");
153 }
154
155 #[test]
156 fn test_script_size() {
157 assert!(resolve_script_size(&sample_tl()) > 0);
158 }
159
160 #[test]
161 fn test_empty_duration() {
162 let tl = new_resolve_timeline("empty", 25.0, ResolveTimelineType::Color);
163 assert_eq!(resolve_duration_frames(&tl), 0);
164 }
165}