oxihuman_export/
edl_export.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone)]
9pub struct EdlEvent {
10 pub event_num: u32,
11 pub reel: String,
12 pub channels: String,
13 pub transition: String,
14 pub src_in: String,
15 pub src_out: String,
16 pub rec_in: String,
17 pub rec_out: String,
18}
19
20#[derive(Debug, Clone)]
22pub struct EdlExport {
23 pub title: String,
24 pub fcm: String, pub events: Vec<EdlEvent>,
26}
27
28pub fn frames_to_timecode(frames: u32, fps: u32) -> String {
30 let fps = fps.max(1);
31 let f = frames % fps;
32 let s = (frames / fps) % 60;
33 let m = (frames / fps / 60) % 60;
34 let h = frames / fps / 3600;
35 format!("{:02}:{:02}:{:02}:{:02}", h, m, s, f)
36}
37
38pub fn new_edl_export(title: &str, drop_frame: bool) -> EdlExport {
40 EdlExport {
41 title: title.to_string(),
42 fcm: if drop_frame {
43 "DROP FRAME".to_string()
44 } else {
45 "NON-DROP FRAME".to_string()
46 },
47 events: Vec::new(),
48 }
49}
50
51#[allow(clippy::too_many_arguments)]
53pub fn edl_add_event(
54 export: &mut EdlExport,
55 reel: &str,
56 channels: &str,
57 transition: &str,
58 src_in: &str,
59 src_out: &str,
60 rec_in: &str,
61 rec_out: &str,
62) {
63 let event_num = export.events.len() as u32 + 1;
64 export.events.push(EdlEvent {
65 event_num,
66 reel: reel.to_string(),
67 channels: channels.to_string(),
68 transition: transition.to_string(),
69 src_in: src_in.to_string(),
70 src_out: src_out.to_string(),
71 rec_in: rec_in.to_string(),
72 rec_out: rec_out.to_string(),
73 });
74}
75
76pub fn edl_event_count(export: &EdlExport) -> usize {
78 export.events.len()
79}
80
81pub fn edl_to_string(export: &EdlExport) -> String {
83 let mut out = format!("TITLE: {}\nFCM: {}\n\n", export.title, export.fcm);
84 for ev in &export.events {
85 out.push_str(&format!(
86 "{:03} {} {} {} {} {} {} {}\n",
87 ev.event_num,
88 ev.reel,
89 ev.channels,
90 ev.transition,
91 ev.src_in,
92 ev.src_out,
93 ev.rec_in,
94 ev.rec_out,
95 ));
96 }
97 out
98}
99
100pub fn edl_size_bytes(export: &EdlExport) -> usize {
102 edl_to_string(export).len()
103}
104
105pub fn events_for_reel<'a>(export: &'a EdlExport, reel: &str) -> Vec<&'a EdlEvent> {
107 export.events.iter().filter(|e| e.reel == reel).collect()
108}
109
110pub fn validate_edl(export: &EdlExport) -> bool {
112 !export.title.is_empty()
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 fn sample_edl() -> EdlExport {
120 let mut exp = new_edl_export("MY_EDIT", false);
121 edl_add_event(
122 &mut exp,
123 "A001",
124 "V",
125 "C",
126 "01:00:00:00",
127 "01:00:04:00",
128 "01:00:00:00",
129 "01:00:04:00",
130 );
131 edl_add_event(
132 &mut exp,
133 "B002",
134 "V",
135 "C",
136 "01:00:04:00",
137 "01:00:08:00",
138 "01:00:04:00",
139 "01:00:08:00",
140 );
141 exp
142 }
143
144 #[test]
145 fn test_event_count() {
146 assert_eq!(edl_event_count(&sample_edl()), 2);
147 }
148
149 #[test]
150 fn test_to_string_title() {
151 assert!(edl_to_string(&sample_edl()).contains("MY_EDIT"));
152 }
153
154 #[test]
155 fn test_to_string_event() {
156 assert!(edl_to_string(&sample_edl()).contains("A001"));
157 }
158
159 #[test]
160 fn test_validate() {
161 assert!(validate_edl(&sample_edl()));
162 }
163
164 #[test]
165 fn test_events_for_reel() {
166 let exp = sample_edl();
167 assert_eq!(events_for_reel(&exp, "A001").len(), 1);
168 assert_eq!(events_for_reel(&exp, "B002").len(), 1);
169 assert_eq!(events_for_reel(&exp, "C003").len(), 0);
170 }
171
172 #[test]
173 fn test_frames_to_timecode() {
174 let tc = frames_to_timecode(0, 24);
175 assert_eq!(tc, "00:00:00:00");
176 }
177
178 #[test]
179 fn test_frames_to_timecode_24fps() {
180 let tc = frames_to_timecode(24, 24);
182 assert_eq!(tc, "00:00:01:00");
183 }
184
185 #[test]
186 fn test_edl_size_positive() {
187 assert!(edl_size_bytes(&sample_edl()) > 0);
188 }
189
190 #[test]
191 fn test_fcm_drop_frame() {
192 let exp = new_edl_export("X", true);
193 assert!(exp.fcm.contains("DROP FRAME"));
194 }
195}