pub mod cea608_decoder;
pub mod cea708_decoder;
pub mod dvb;
pub mod eia608_realtime;
pub mod pgs;
pub mod srt;
pub mod ssa;
pub mod ttml;
pub mod webvtt;
#[cfg(feature = "closed-captions")]
pub mod cea608;
use crate::{SubtitleError, SubtitleResult};
pub fn detect_format(data: &[u8], filename: Option<&str>) -> SubtitleResult<SubtitleFormat> {
if let Some(name) = filename {
let lower = name.to_lowercase();
if lower.ends_with(".srt") {
return Ok(SubtitleFormat::Srt);
}
if lower.ends_with(".vtt") || lower.ends_with(".webvtt") {
return Ok(SubtitleFormat::WebVtt);
}
if lower.ends_with(".ssa") {
return Ok(SubtitleFormat::Ssa);
}
if lower.ends_with(".ass") {
return Ok(SubtitleFormat::Ass);
}
if lower.ends_with(".ttml") || lower.ends_with(".xml") {
return Ok(SubtitleFormat::Ttml);
}
if lower.ends_with(".sup") {
return Ok(SubtitleFormat::Pgs);
}
}
let text = String::from_utf8_lossy(data);
if text.starts_with("WEBVTT") {
return Ok(SubtitleFormat::WebVtt);
}
if text.contains("[Script Info]") {
if text.contains("ScriptType: v4.00+") {
return Ok(SubtitleFormat::Ass);
}
return Ok(SubtitleFormat::Ssa);
}
if text.trim_start().starts_with("<?xml") && text.contains("<tt") {
return Ok(SubtitleFormat::Ttml);
}
if data.len() >= 2 && &data[0..2] == b"PG" {
return Ok(SubtitleFormat::Pgs);
}
if !data.is_empty() && data[0] == 0x0F {
return Ok(SubtitleFormat::Dvb);
}
if srt::is_srt_format(&text) {
return Ok(SubtitleFormat::Srt);
}
Err(SubtitleError::InvalidFormat(
"Cannot detect subtitle format".to_string(),
))
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SubtitleFormat {
Srt,
WebVtt,
Ssa,
Ass,
Ttml,
#[cfg(feature = "closed-captions")]
Cea608,
#[cfg(feature = "closed-captions")]
Cea708,
Dvb,
Pgs,
}
impl SubtitleFormat {
#[must_use]
pub const fn extension(&self) -> &'static str {
match self {
Self::Srt => "srt",
Self::WebVtt => "vtt",
Self::Ssa => "ssa",
Self::Ass => "ass",
Self::Ttml => "ttml",
#[cfg(feature = "closed-captions")]
Self::Cea608 => "608",
#[cfg(feature = "closed-captions")]
Self::Cea708 => "708",
Self::Dvb => "dvb",
Self::Pgs => "sup",
}
}
#[must_use]
pub const fn name(&self) -> &'static str {
match self {
Self::Srt => "SubRip",
Self::WebVtt => "WebVTT",
Self::Ssa => "SubStation Alpha",
Self::Ass => "Advanced SubStation Alpha",
Self::Ttml => "Timed Text Markup Language",
#[cfg(feature = "closed-captions")]
Self::Cea608 => "CEA-608",
#[cfg(feature = "closed-captions")]
Self::Cea708 => "CEA-708",
Self::Dvb => "DVB Subtitles",
Self::Pgs => "PGS (Blu-ray)",
}
}
}