use super::format::{Format, SubtitleFile};
use super::subtitle::Subtitle;
#[cfg(any(feature = "ass", feature = "ssa"))]
use super::types::{AssData, AssStyle};
#[derive(Debug, Clone)]
pub struct SubtitleFileBuilder {
format: Format,
subtitles: Vec<Subtitle>,
fps: Option<f64>,
header: Option<String>,
#[cfg(any(feature = "ass", feature = "ssa"))]
styles: Vec<AssStyle>,
}
impl SubtitleFileBuilder {
pub fn new(format: Format) -> Self {
Self {
format,
subtitles: Vec::new(),
fps: None,
header: None,
#[cfg(any(feature = "ass", feature = "ssa"))]
styles: Vec::new(),
}
}
pub fn add_subtitle(mut self, subtitle: Subtitle) -> Self {
self.subtitles.push(subtitle);
self
}
pub fn add_subtitles(mut self, subtitles: impl IntoIterator<Item = Subtitle>) -> Self {
self.subtitles.extend(subtitles);
self
}
pub fn with_fps(mut self, fps: f64) -> Self {
self.fps = Some(fps);
self
}
pub fn with_header(mut self, header: impl Into<String>) -> Self {
self.header = Some(header.into());
self
}
#[cfg(any(feature = "ass", feature = "ssa"))]
pub fn add_style(mut self, style: AssStyle) -> Self {
self.styles.push(style);
self
}
#[cfg(any(feature = "ass", feature = "ssa"))]
pub fn add_styles(mut self, styles: impl IntoIterator<Item = AssStyle>) -> Self {
self.styles.extend(styles);
self
}
pub fn build(self) -> Option<SubtitleFile> {
match self.format {
#[cfg(feature = "srt")]
Format::Srt => Some(SubtitleFile::Srt(self.subtitles)),
#[cfg(feature = "vtt")]
Format::Vtt => Some(SubtitleFile::Vtt {
header: self.header,
subtitles: self.subtitles,
}),
#[cfg(feature = "ass")]
Format::Ass => Some(SubtitleFile::Ass(AssData {
info: std::collections::HashMap::new(),
styles: if self.styles.is_empty() {
vec![AssStyle::default_style()]
} else {
self.styles
},
subtitles: self.subtitles,
})),
#[cfg(feature = "ssa")]
Format::Ssa => Some(SubtitleFile::Ssa(AssData {
info: std::collections::HashMap::new(),
styles: if self.styles.is_empty() {
vec![AssStyle::default_style()]
} else {
self.styles
},
subtitles: self.subtitles,
})),
#[cfg(feature = "microdvd")]
Format::MicroDvd => {
let fps = self.fps?;
Some(SubtitleFile::MicroDvd {
fps,
subtitles: self.subtitles,
})
}
#[cfg(feature = "subviewer")]
Format::SubViewer => Some(SubtitleFile::SubViewer {
header: self.header,
subtitles: self.subtitles,
}),
#[cfg(feature = "ttml")]
Format::Ttml => Some(SubtitleFile::Ttml {
header: self.header,
subtitles: self.subtitles,
}),
#[cfg(feature = "sbv")]
Format::Sbv => Some(SubtitleFile::Sbv(self.subtitles)),
#[cfg(feature = "lrc")]
Format::Lrc => {
let data = crate::lrc::LrcData::default();
let flatten = data.to_subtitles();
Some(SubtitleFile::Lrc {
data,
subtitles: if self.subtitles.is_empty() {
flatten
} else {
self.subtitles
},
})
}
#[cfg(feature = "sami")]
Format::Sami => Some(SubtitleFile::Sami(crate::sami::SamiData {
header: self.header,
styles: std::collections::HashMap::new(),
subtitles: self.subtitles,
})),
#[cfg(feature = "mpl2")]
Format::Mpl2 => Some(SubtitleFile::Mpl2(self.subtitles)),
#[cfg(feature = "scc")]
Format::Scc => Some(SubtitleFile::Scc(crate::scc::SccData {
fps: crate::scc::DEFAULT_FPS,
drop_frame: true,
subtitles: self.subtitles,
})),
#[cfg(feature = "ebu_stl")]
Format::EbuStl => {
let tti_blocks: Vec<crate::ebu_stl::TtiBlock> = self
.subtitles
.iter()
.enumerate()
.map(|(i, sub)| crate::ebu_stl::TtiBlock {
subtitle_group: 0,
subtitle_number: (i + 1) as u16,
extension_block: 0,
cumulative_status: 0,
timecode_start: sub.start as u32,
timecode_end: sub.end as u32,
vertical_position: 20,
justification: 0,
comment_flag: false,
text: sub.text.clone(),
})
.collect();
Some(SubtitleFile::EbuStl(Box::new(crate::ebu_stl::EbuStlData {
gsi: crate::ebu_stl::GsiBlock::default(),
subtitles: self.subtitles,
tti_blocks,
})))
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ParseConfig {
pub preserve_indices: bool,
pub lenient_mode: bool,
pub auto_detect_encoding: bool,
pub max_duration_ms: u64,
pub min_duration_ms: u64,
}
impl Default for ParseConfig {
fn default() -> Self {
Self {
preserve_indices: false,
lenient_mode: false,
auto_detect_encoding: true,
max_duration_ms: 0,
min_duration_ms: 0,
}
}
}
impl ParseConfig {
pub fn new() -> Self {
Self::default()
}
pub fn preserve_indices(mut self, preserve: bool) -> Self {
self.preserve_indices = preserve;
self
}
pub fn lenient_mode(mut self, lenient: bool) -> Self {
self.lenient_mode = lenient;
self
}
pub fn auto_detect_encoding(mut self, detect: bool) -> Self {
self.auto_detect_encoding = detect;
self
}
pub fn max_duration_ms(mut self, ms: u64) -> Self {
self.max_duration_ms = ms;
self
}
pub fn min_duration_ms(mut self, ms: u64) -> Self {
self.min_duration_ms = ms;
self
}
}