psyche-subtitle-toolkit 0.3.0

Extract, translate, and mux ASS/SRT/VTT/PGS subtitles in MKV files via pluggable translation providers
/// Anthropic Messages API provider (`/v1/messages`).
pub mod anthropic;
/// DeepL Translation API provider (`/v2/translate`).
pub mod deepl;
/// Google Gemini `generateContent` provider.
pub mod gemini;
/// Google Cloud Translation v2 provider (`/language/translate/v2`).
pub mod google;
/// Ollama local LLM provider (`/api/generate`).
pub mod ollama;
/// OpenAI Chat Completions provider (`/v1/chat/completions`).
pub mod openai;
/// OpenRouter unified LLM provider (`/api/v1/chat/completions`).
pub mod openrouter;

use async_trait::async_trait;

use crate::error::Result;

/// A translation backend.
///
/// Implement this trait to provide custom translation providers.
/// Built-in implementations:
/// - [`anthropic::AnthropicTranslator`] — calls the Anthropic Messages API
/// - [`ollama::OllamaTranslator`] — calls the Ollama `/api/generate` endpoint
/// - [`openai::OpenAiTranslator`] — calls the OpenAI `/v1/chat/completions` endpoint
/// - [`deepl::DeepLTranslator`] — calls the DeepL `/v2/translate` endpoint
/// - [`google::GoogleTranslator`] — calls the Google Cloud Translation v2 endpoint
/// - [`gemini::GeminiTranslator`] — calls the Google Gemini `generateContent` endpoint
/// - [`openrouter::OpenRouterTranslator`] — calls the OpenRouter `/api/v1/chat/completions` endpoint
///
/// Each provider owns its own prompt construction and HTTP client.
/// The pipeline calls [`Translator::translate`] with numbered subtitle text
/// in `<N> text` format and expects the same format back.
#[async_trait]
pub trait Translator: Send + Sync {
    /// Translate the source text and return the translated string.
    ///
    /// The returned string should preserve the `<N>` numbered line format
    /// from the input.
    async fn translate(&self, request: TranslationRequest<'_>) -> Result<String>;
}

/// A translation request sent to a [`Translator`].
#[derive(Debug, Clone)]
pub struct TranslationRequest<'a> {
    /// Numbered subtitle dialogue text in `<N> text` format.
    pub source_text: &'a str,
    /// Target language code (e.g. `"pt-BR"`, `"en"`, `"ja"`).
    pub target_language: &'a str,
}