subx_cli/core/formats/
transformers.rs

1use crate::core::formats::converter::FormatConverter;
2use crate::core::formats::{Subtitle, SubtitleFormatType};
3
4impl FormatConverter {
5    /// 轉換字幕物件至目標格式
6    pub(crate) fn transform_subtitle(
7        &self,
8        subtitle: Subtitle,
9        target_format: &str,
10    ) -> crate::Result<Subtitle> {
11        match (subtitle.format.as_str(), target_format) {
12            ("srt", "ass") => self.srt_to_ass(subtitle),
13            ("ass", "srt") => self.ass_to_srt(subtitle),
14            ("srt", "vtt") => self.srt_to_vtt(subtitle),
15            ("vtt", "srt") => self.vtt_to_srt(subtitle),
16            ("ass", "vtt") => self.ass_to_vtt(subtitle),
17            ("vtt", "ass") => self.vtt_to_ass(subtitle),
18            (source, target) if source == target => Ok(subtitle),
19            _ => Err(crate::error::SubXError::subtitle_format(
20                subtitle.format.to_string(),
21                format!("不支援的轉換: {} -> {}", subtitle.format, target_format),
22            )),
23        }
24    }
25
26    /// SRT 轉 ASS
27    pub(crate) fn srt_to_ass(&self, mut subtitle: Subtitle) -> crate::Result<Subtitle> {
28        let _default_style = crate::core::formats::ass::AssStyle {
29            name: "Default".to_string(),
30            font_name: "Arial".to_string(),
31            font_size: 16,
32            primary_color: crate::core::formats::ass::Color::white(),
33            secondary_color: crate::core::formats::ass::Color::red(),
34            outline_color: crate::core::formats::ass::Color::black(),
35            shadow_color: crate::core::formats::ass::Color::black(),
36            bold: false,
37            italic: false,
38            underline: false,
39            alignment: 2,
40        };
41        for entry in &mut subtitle.entries {
42            if self.config.preserve_styling {
43                entry.styling = Some(self.extract_srt_styling(&entry.text)?);
44            }
45            entry.text = self.convert_srt_tags_to_ass(&entry.text);
46        }
47        subtitle.format = SubtitleFormatType::Ass;
48        subtitle.metadata.original_format = SubtitleFormatType::Srt;
49        Ok(subtitle)
50    }
51
52    /// ASS 轉 SRT
53    pub(crate) fn ass_to_srt(&self, mut subtitle: Subtitle) -> crate::Result<Subtitle> {
54        for entry in &mut subtitle.entries {
55            entry.text = self.strip_ass_tags(&entry.text);
56            if self.config.preserve_styling {
57                entry.text = self.convert_ass_tags_to_srt(&entry.text);
58            }
59            entry.styling = None;
60        }
61        subtitle.format = SubtitleFormatType::Srt;
62        Ok(subtitle)
63    }
64
65    /// SRT 轉 VTT
66    pub(crate) fn srt_to_vtt(&self, mut subtitle: Subtitle) -> crate::Result<Subtitle> {
67        subtitle.metadata.title = Some("WEBVTT".to_string());
68        for entry in &mut subtitle.entries {
69            entry.text = self.convert_srt_tags_to_vtt(&entry.text);
70        }
71        subtitle.format = SubtitleFormatType::Vtt;
72        Ok(subtitle)
73    }
74
75    /// ASS 轉 VTT
76    pub(crate) fn ass_to_vtt(&self, subtitle: Subtitle) -> crate::Result<Subtitle> {
77        // 先將 ASS 轉為 SRT,再轉為 VTT
78        let subtitle = self.ass_to_srt(subtitle)?;
79        self.srt_to_vtt(subtitle)
80    }
81
82    /// VTT 轉 SRT
83    pub(crate) fn vtt_to_srt(&self, mut subtitle: Subtitle) -> crate::Result<Subtitle> {
84        // VTT 可保留或移除 HTML 樣式標籤
85        for entry in &mut subtitle.entries {
86            if self.config.preserve_styling {
87                entry.text = self.convert_vtt_tags_to_srt(&entry.text);
88            } else {
89                entry.text = self.strip_vtt_tags(&entry.text);
90            }
91            entry.styling = None;
92        }
93        subtitle.format = SubtitleFormatType::Srt;
94        Ok(subtitle)
95    }
96
97    /// VTT 轉 ASS
98    pub(crate) fn vtt_to_ass(&self, subtitle: Subtitle) -> crate::Result<Subtitle> {
99        // 先將 VTT 轉為 SRT,再轉為 ASS
100        let subtitle = self.vtt_to_srt(subtitle)?;
101        self.srt_to_ass(subtitle)
102    }
103}