use serde::Deserialize;
use serde::Serialize;
use std::fmt::Display;
use std::str;
use time::format_description::BorrowedFormatItem;
use time::macros::format_description;
use crate::error;
use time::Time;
use super::ssa::SSA;
use super::strip_bom;
use super::vtt::{VTTLine, VTT};
const TIME_FORMAT: &[BorrowedFormatItem] =
format_description!("[hour]:[minute]:[second],[subsecond digits:3]");
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct SRT {
pub lines: Vec<SRTLine>,
}
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct SRTLine {
pub sequence_number: u32,
pub start: Time,
pub end: Time,
pub text: String,
}
impl SRT {
pub fn parse<S: AsRef<str>>(content: S) -> Result<SRT, SRTError> {
let mut line_num = 0;
let mut blocks = vec![vec![]];
for line in strip_bom(&content).lines() {
if line.trim().is_empty() {
if !blocks.last().unwrap().is_empty() {
blocks.push(vec![])
}
} else {
blocks.last_mut().unwrap().push(line)
}
}
if blocks.last().is_some_and(|b| b.is_empty()) {
blocks.remove(blocks.len() - 1);
}
let mut lines = vec![];
for block in blocks {
line_num += 1;
let mut block_lines = block.into_iter();
let sequence_number = block_lines
.next()
.ok_or(SRTError::new(
SRTErrorKind::Parse("invalid sequence number".to_string()),
line_num,
))?
.trim()
.parse::<u32>()
.map_err(|e| SRTError::new(SRTErrorKind::Parse(e.to_string()), line_num))?;
line_num += 1;
let (start, end) = {
let (start, end) = block_lines
.next()
.ok_or(SRTError::new(
SRTErrorKind::Parse("invalid time range".to_string()),
line_num,
))?
.split_once("-->")
.ok_or(SRTError::new(
SRTErrorKind::Parse("invalid time range".to_string()),
line_num,
))?;
let start_time = Time::parse(start.trim(), TIME_FORMAT)
.map_err(|e| SRTError::new(SRTErrorKind::Parse(e.to_string()), line_num))?;
let end_time = Time::parse(end.trim(), TIME_FORMAT)
.map_err(|e| SRTError::new(SRTErrorKind::Parse(e.to_string()), line_num))?;
(start_time, end_time)
};
line_num += 1;
line_num += block_lines.len();
let text = block_lines.collect::<Vec<&str>>().join("\r\n");
lines.push(SRTLine {
sequence_number,
start,
end,
text,
})
}
Ok(SRT { lines })
}
pub fn to_ssa(&self) -> SSA {
self.to_vtt().to_ssa()
}
pub fn to_vtt(&self) -> VTT {
VTT {
regions: vec![],
styles: vec![],
lines: self
.lines
.iter()
.map(|l| VTTLine {
identifier: Some(l.sequence_number.to_string()),
start: l.start,
end: l.end,
text: l.text.replace("\r\n", "\n"),
..Default::default()
})
.collect(),
}
}
}
impl Display for SRT {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut blocks = vec![];
for line in &self.lines {
let mut block = vec![];
block.push(line.sequence_number.to_string());
block.push(format!(
"{} --> {}",
line.start.format(TIME_FORMAT).unwrap(),
line.end.format(TIME_FORMAT).unwrap()
));
block.push(line.text.clone());
blocks.push(block)
}
write!(
f,
"{}",
blocks
.into_iter()
.map(|b| b.join("\r\n"))
.collect::<Vec<String>>()
.join("\r\n\r\n")
)
}
}
error! {
SRTError => SRTErrorKind {
Parse(String),
}
}