Skip to main content

subtitler/
lib.rs

1#[cfg(feature = "ass")]
2pub mod ass;
3pub mod config;
4#[cfg(feature = "ebu_stl")]
5pub mod ebu_stl;
6pub mod encoding;
7pub mod error;
8#[cfg(feature = "lrc")]
9pub mod lrc;
10#[cfg(feature = "microdvd")]
11pub mod microdvd;
12pub mod model;
13#[cfg(feature = "mpl2")]
14pub mod mpl2;
15pub mod normalize;
16pub mod pipeline;
17pub mod quality;
18#[cfg(feature = "sami")]
19pub mod sami;
20#[cfg(feature = "sbv")]
21pub mod sbv;
22#[cfg(feature = "scc")]
23pub mod scc;
24#[cfg(feature = "srt")]
25pub mod srt;
26#[cfg(feature = "subviewer")]
27pub mod subviewer;
28#[cfg(feature = "ttml")]
29pub mod ttml;
30pub mod types;
31pub mod utils;
32#[cfg(feature = "vtt")]
33pub mod vtt;
34
35pub use model::SubtitleFormat;
36
37// Re-export commonly used types for convenience
38pub use model::{
39  Format, ParseConfig, StreamingParser, Subtitle, SubtitleFile, SubtitleFileBuilder, TextPart,
40  WritePolicy,
41};
42
43pub fn detect_format(data: &[u8]) -> Option<Format> {
44  #[cfg(feature = "srt")]
45  let f = srt::detect_format(data);
46  #[cfg(not(feature = "srt"))]
47  let f: Option<Format> = None;
48
49  #[cfg(feature = "vtt")]
50  let f = f.or_else(|| vtt::detect_format(data));
51  #[cfg(feature = "ass")]
52  let f = f.or_else(|| ass::detect_format(data));
53  #[cfg(feature = "microdvd")]
54  let f = f.or_else(|| microdvd::detect_format(data));
55  #[cfg(feature = "subviewer")]
56  let f = f.or_else(|| subviewer::detect_format(data));
57  #[cfg(feature = "ttml")]
58  let f = f.or_else(|| ttml::detect_format(data));
59  #[cfg(feature = "sbv")]
60  let f = f.or_else(|| sbv::detect_format(data));
61  #[cfg(feature = "lrc")]
62  let f = f.or_else(|| lrc::detect_format(data));
63  #[cfg(feature = "sami")]
64  let f = f.or_else(|| sami::detect_format(data));
65  #[cfg(feature = "mpl2")]
66  let f = f.or_else(|| mpl2::detect_format(data));
67  #[cfg(feature = "scc")]
68  let f = f.or_else(|| scc::detect_format(data));
69  #[cfg(feature = "ebu_stl")]
70  let f = f.or_else(|| ebu_stl::detect_format(data));
71  f
72}
73
74/// Parse bytes into a `SubtitleFile`, auto-detecting the format.
75pub fn parse_bytes(data: &[u8]) -> Result<model::SubtitleFile, error::ParseError> {
76  let fmt = detect_format(data).ok_or(error::ParseError::UnknownFormat)?;
77  parse_bytes_as(data, fmt)
78}
79
80/// Parse bytes as a specific format.
81pub fn parse_bytes_as(data: &[u8], fmt: Format) -> Result<model::SubtitleFile, error::ParseError> {
82  match fmt {
83    #[cfg(feature = "srt")]
84    Format::Srt => Ok(srt::parse_bytes(data)?),
85    #[cfg(feature = "vtt")]
86    Format::Vtt => Ok(vtt::parse_bytes(data)?),
87    #[cfg(feature = "ass")]
88    Format::Ass => Ok(ass::parse_bytes(data)?),
89    #[cfg(feature = "ssa")]
90    Format::Ssa => match ass::parse_bytes(data)? {
91      model::SubtitleFile::Ass(data) => Ok(model::SubtitleFile::Ssa(data)),
92      other => Ok(other),
93    },
94    #[cfg(feature = "microdvd")]
95    Format::MicroDvd => {
96      let file = microdvd::parse_bytes(data, None)?;
97      Ok(file)
98    }
99    #[cfg(feature = "subviewer")]
100    Format::SubViewer => Ok(subviewer::parse_bytes(data)?),
101    #[cfg(feature = "ttml")]
102    Format::Ttml => Ok(ttml::parse_bytes(data)?),
103    #[cfg(feature = "sbv")]
104    Format::Sbv => Ok(sbv::parse_bytes(data)?),
105    #[cfg(feature = "lrc")]
106    Format::Lrc => Ok(lrc::parse_bytes(data)?),
107    #[cfg(feature = "sami")]
108    Format::Sami => Ok(sami::parse_bytes(data)?),
109    #[cfg(feature = "mpl2")]
110    Format::Mpl2 => Ok(mpl2::parse_bytes(data)?),
111    #[cfg(feature = "scc")]
112    Format::Scc => Ok(scc::parse_bytes(data)?),
113    #[cfg(feature = "ebu_stl")]
114    Format::EbuStl => Ok(ebu_stl::parse_bytes(data)?),
115    #[allow(unreachable_patterns)]
116    _ => Err(error::ParseError::Unsupported(fmt)),
117  }
118}
119
120/// Parse a file into a `SubtitleFile`, auto-detecting the format.
121#[cfg(not(target_arch = "wasm32"))]
122pub async fn parse_file(
123  path: impl AsRef<std::path::Path>,
124) -> Result<model::SubtitleFile, error::ParseError> {
125  let data = tokio::fs::read(path).await?;
126  parse_bytes(&data)
127}
128
129/// Parse a URL into a `SubtitleFile`, auto-detecting the format (requires
130/// the `http` feature).
131#[cfg(feature = "http")]
132pub async fn parse_url(url: &str) -> Result<model::SubtitleFile, error::ParseError> {
133  let client = reqwest::Client::new();
134  parse_url_with(url, &client).await
135}
136
137/// Parse a URL with a custom `reqwest::Client`, allowing configuration of
138/// timeouts, redirect policy, TLS options, etc.
139#[cfg(feature = "http")]
140pub async fn parse_url_with(
141  url: &str,
142  client: &reqwest::Client,
143) -> Result<model::SubtitleFile, error::ParseError> {
144  let response = client.get(url).send().await?;
145  let bytes = response.bytes().await?;
146  parse_bytes(&bytes)
147}
148
149#[cfg(target_arch = "wasm32")]
150pub mod wasm;