pub mod config;
pub mod domain;
pub mod error;
pub mod formats;
pub mod media;
pub mod ocr;
pub mod pipeline;
pub mod providers;
use std::path::Path;
use std::sync::Arc;
use tokio::fs;
pub use config::{AppConfig, LoadedConfig, ToolConfig, TranslationDefaults, default_config_path};
pub use domain::{
AssClassificationPolicy, CueClassification, CueDisposition, CueKind, ProviderConfig,
SubtitleClassificationEntry, SubtitleClassificationReport, SubtitleClassificationSummary,
SubtitleCue, SubtitleDocument, SubtitleFormat, ThinkingMode, TranslationOptions,
TranslationResult,
};
pub use error::TranslatorError;
pub use formats::{classify_document, parse_subtitle, render_subtitle};
pub use media::{
ExternalToolConfig, MediaInputKind, PgsExtractionResult, SelectedSubtitleStream,
StreamDisposition, VideoSubtitleJob, count_subtitle_streams, detect_input_kind,
extract_pgs_stream, is_supported_input_path, mux_translated_subtitle, prepare_video_subtitle_job,
};
pub use ocr::{
OcrDebugResult, PgsOcrConfig, PgsOcrLanguage, cleanup_fragmentary_subtitle_document,
debug_pgs_ocr, ocr_pgs_file_to_srt,
};
pub use providers::{
OpenAiCompatibleProvider, ProviderCapabilities, TranslatedItem, TranslationBatch,
TranslationBatchItem, TranslationBatchOutput, TranslationProvider,
};
#[derive(Clone)]
pub struct Translator {
provider: Arc<dyn TranslationProvider>,
}
impl Translator {
pub fn new(provider: Arc<dyn TranslationProvider>) -> Self {
Self { provider }
}
pub async fn translate_document(
&self,
document: &SubtitleDocument,
options: &TranslationOptions,
) -> Result<TranslationResult, TranslatorError> {
pipeline::translate::translate_document(self.provider.as_ref(), document, options).await
}
pub async fn translate_str(
&self,
source: &str,
format: SubtitleFormat,
options: &TranslationOptions,
) -> Result<TranslationResult, TranslatorError> {
self.translate_source(source, Some(format), None, options)
.await
}
pub async fn translate_source(
&self,
source: &str,
format_hint: Option<SubtitleFormat>,
source_name: Option<&Path>,
options: &TranslationOptions,
) -> Result<TranslationResult, TranslatorError> {
let document = formats::parse_subtitle(source, format_hint, source_name)?;
self.translate_document(&document, options).await
}
pub async fn translate_file_path(
&self,
path: impl AsRef<Path>,
options: &TranslationOptions,
) -> Result<TranslationResult, TranslatorError> {
let path = path.as_ref();
let source = fs::read_to_string(path).await?;
let format_hint = SubtitleFormat::detect_from_path(path);
self.translate_source(&source, format_hint, Some(path), options)
.await
}
}
#[cfg(feature = "blocking")]
pub struct BlockingTranslator {
translator: Translator,
runtime: tokio::runtime::Runtime,
}
#[cfg(feature = "blocking")]
impl BlockingTranslator {
pub fn new(translator: Translator) -> Result<Self, TranslatorError> {
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.map_err(|error| TranslatorError::RuntimeInit(error.to_string()))?;
Ok(Self {
translator,
runtime,
})
}
pub fn translate_document(
&self,
document: &SubtitleDocument,
options: &TranslationOptions,
) -> Result<TranslationResult, TranslatorError> {
self.runtime
.block_on(self.translator.translate_document(document, options))
}
pub fn translate_str(
&self,
source: &str,
format: SubtitleFormat,
options: &TranslationOptions,
) -> Result<TranslationResult, TranslatorError> {
self.runtime
.block_on(self.translator.translate_str(source, format, options))
}
pub fn translate_file_path(
&self,
path: impl AsRef<Path>,
options: &TranslationOptions,
) -> Result<TranslationResult, TranslatorError> {
self.runtime
.block_on(self.translator.translate_file_path(path, options))
}
pub fn translate_source(
&self,
source: &str,
format_hint: Option<SubtitleFormat>,
source_name: Option<&Path>,
options: &TranslationOptions,
) -> Result<TranslationResult, TranslatorError> {
self.runtime
.block_on(self.translator.translate_source(source, format_hint, source_name, options))
}
}