1use super::SrtFormat;
4use crate::core::{EditorDocument, EditorError};
5use crate::formats::{FormatImporter, FormatInfo, FormatOptions, FormatResult};
6use ass_core::parser::Script;
7use std::io::Read;
8
9impl SrtFormat {
10 fn parse_srt_subtitle(
12 lines: &[String],
13 start_idx: usize,
14 ) -> Result<(usize, String), EditorError> {
15 if start_idx >= lines.len() {
16 return Err(EditorError::InvalidFormat(
17 "Unexpected end of file".to_string(),
18 ));
19 }
20
21 let mut idx = start_idx;
22
23 while idx < lines.len() && lines[idx].trim().is_empty() {
25 idx += 1;
26 }
27
28 if idx >= lines.len() {
29 return Err(EditorError::InvalidFormat(
30 "Unexpected end of file".to_string(),
31 ));
32 }
33
34 let _subtitle_num = lines[idx].trim();
36 idx += 1;
37
38 if idx >= lines.len() {
39 return Err(EditorError::InvalidFormat(
40 "Missing timestamp line".to_string(),
41 ));
42 }
43
44 let timestamp_line = &lines[idx];
46 if !timestamp_line.contains("-->") {
47 return Err(EditorError::InvalidFormat(format!(
48 "Invalid timestamp line: {timestamp_line}"
49 )));
50 }
51
52 let parts: Vec<&str> = timestamp_line.split("-->").collect();
53 if parts.len() != 2 {
54 return Err(EditorError::InvalidFormat(format!(
55 "Invalid timestamp format: {timestamp_line}"
56 )));
57 }
58
59 let start_time = Self::parse_srt_time(parts[0])?;
60 let end_time = Self::parse_srt_time(parts[1])?;
61
62 idx += 1;
63
64 let mut text_lines = Vec::new();
66 while idx < lines.len() && !lines[idx].trim().is_empty() {
67 let styled_text = Self::convert_srt_to_ass_styling(&lines[idx]);
68 text_lines.push(styled_text);
69 idx += 1;
70 }
71
72 if text_lines.is_empty() {
73 return Err(EditorError::InvalidFormat(
74 "Empty subtitle text".to_string(),
75 ));
76 }
77
78 let text = text_lines.join("\\N"); let dialogue_line = format!("Dialogue: 0,{start_time},{end_time},Default,,0,0,0,,{text}");
80
81 Ok((idx, dialogue_line))
82 }
83}
84
85impl FormatImporter for SrtFormat {
86 fn format_info(&self) -> &FormatInfo {
87 &self.info
88 }
89
90 fn import_from_reader(
91 &self,
92 reader: &mut dyn Read,
93 options: &FormatOptions,
94 ) -> Result<(EditorDocument, FormatResult), EditorError> {
95 let mut content = String::new();
97 reader
98 .read_to_string(&mut content)
99 .map_err(|e| EditorError::IoError(format!("Failed to read SRT content: {e}")))?;
100
101 let lines: Vec<String> = content.lines().map(|s| s.to_string()).collect();
102 let mut warnings = Vec::new();
103 let mut dialogues = Vec::new();
104 let mut idx = 0;
105 let mut subtitle_count = 0;
106
107 while idx < lines.len() {
109 match Self::parse_srt_subtitle(&lines, idx) {
110 Ok((next_idx, dialogue)) => {
111 dialogues.push(dialogue);
112 idx = next_idx;
113 subtitle_count += 1;
114 }
115 Err(e) => {
116 if idx < lines.len() {
117 warnings.push(format!(
118 "Skipping invalid subtitle at line {}: {e}",
119 idx + 1
120 ));
121 idx += 1;
122 } else {
123 break;
124 }
125 }
126 }
127 }
128
129 let mut ass_content = String::new();
131
132 ass_content.push_str("[Script Info]\n");
134 ass_content.push_str("Title: Converted from SRT\n");
135 ass_content.push_str("ScriptType: v4.00+\n");
136 ass_content.push_str("Collisions: Normal\n");
137 ass_content.push_str("PlayDepth: 0\n");
138 ass_content.push_str("Timer: 100.0000\n");
139 ass_content.push_str("Video Aspect Ratio: 0\n");
140 ass_content.push_str("Video Zoom: 6\n");
141 ass_content.push_str("Video Position: 0\n\n");
142
143 ass_content.push_str("[V4+ Styles]\n");
145 ass_content.push_str("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n");
146 ass_content.push_str("Style: Default,Arial,20,&H00FFFFFF,&H000000FF,&H00000000,&H80000000,0,0,0,0,100,100,0,0,1,2,0,2,10,10,10,1\n\n");
147
148 ass_content.push_str("[Events]\n");
150 ass_content.push_str(
151 "Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n",
152 );
153
154 for dialogue in dialogues {
155 ass_content.push_str(&dialogue);
156 ass_content.push('\n');
157 }
158
159 let _script = Script::parse(&ass_content)?;
161
162 let document = EditorDocument::from_content(&ass_content)?;
164
165 let mut result = FormatResult::success(subtitle_count)
167 .with_metadata("original_format".to_string(), "SRT".to_string())
168 .with_metadata("subtitles_count".to_string(), subtitle_count.to_string())
169 .with_metadata("encoding".to_string(), options.encoding.clone());
170
171 if !warnings.is_empty() {
172 result = result.with_warnings(warnings);
173 }
174
175 Ok((document, result))
176 }
177}