ass_editor/formats/webvtt/
exporter.rs1use crate::core::{EditorDocument, EditorError};
7use crate::formats::{FormatExporter, FormatInfo, FormatOptions, FormatResult};
8use std::io::Write;
9
10use super::WebVttFormat;
11
12impl FormatExporter for WebVttFormat {
13 fn format_info(&self) -> &FormatInfo {
14 &self.info
15 }
16
17 fn export_to_writer(
18 &self,
19 document: &EditorDocument,
20 writer: &mut dyn Write,
21 options: &FormatOptions,
22 ) -> Result<FormatResult, EditorError> {
23 let events = document.parse_script_with(|script| {
25 if let Some(ass_core::parser::ast::Section::Events(events)) =
27 script.find_section(ass_core::parser::ast::SectionType::Events)
28 {
29 events
31 .iter()
32 .map(|event| {
33 (
34 event.event_type,
35 event.start.to_string(),
36 event.end.to_string(),
37 event.text.to_string(),
38 )
39 })
40 .collect::<Vec<_>>()
41 } else {
42 Vec::new()
43 }
44 })?;
45
46 let mut vtt_content = String::new();
47 let mut cue_num = 1;
48 let mut warnings = Vec::new();
49
50 vtt_content.push_str("WEBVTT\n\n");
52
53 for (event_type, start, end, text) in &events {
54 if event_type.as_str() != "Dialogue" {
56 continue;
57 }
58
59 let start_time = match Self::format_vtt_time(start) {
61 Ok(time) => time,
62 Err(e) => {
63 warnings.push(format!("Invalid start time for cue {cue_num}: {e}"));
64 continue;
65 }
66 };
67
68 let end_time = match Self::format_vtt_time(end) {
69 Ok(time) => time,
70 Err(e) => {
71 warnings.push(format!("Invalid end time for cue {cue_num}: {e}"));
72 continue;
73 }
74 };
75
76 let mut text = text.clone();
78
79 text = text.replace("\\N", "\n");
81 text = text.replace("\\n", "\n");
82
83 text = Self::convert_ass_to_vtt_styling(&text);
85
86 vtt_content.push_str(&format!("{cue_num}\n"));
88 vtt_content.push_str(&format!("{start_time} --> {end_time}\n"));
89 vtt_content.push_str(&text);
90 vtt_content.push_str("\n\n");
91
92 cue_num += 1;
93 }
94
95 let bytes = if options.encoding.eq_ignore_ascii_case("UTF-8") {
97 vtt_content.into_bytes()
98 } else {
99 warnings.push(format!(
100 "Encoding '{}' not supported, using UTF-8 instead",
101 options.encoding
102 ));
103 vtt_content.into_bytes()
104 };
105
106 writer
107 .write_all(&bytes)
108 .map_err(|e| EditorError::IoError(format!("Failed to write WebVTT content: {e}")))?;
109
110 let mut result = FormatResult::success(cue_num - 1)
111 .with_metadata("exported_format".to_string(), "WebVTT".to_string())
112 .with_metadata("cues_exported".to_string(), (cue_num - 1).to_string());
113
114 if !warnings.is_empty() {
115 result = result.with_warnings(warnings);
116 }
117
118 Ok(result)
119 }
120}