Expand description
§psyche-subtitle-toolkit
Extract, translate, and mux ASS subtitles in MKV files. Built for Psyche but usable as a standalone CLI or library.
No cloud required. No telemetry. Every translation provider is opt-in.
§Supported providers
| Provider | Struct | Endpoint |
|---|---|---|
| Ollama | OllamaTranslator | /api/generate |
| OpenAI | OpenAiTranslator | /v1/chat/completions |
| OpenRouter | OpenRouterTranslator | /api/v1/chat/completions |
| Anthropic | AnthropicTranslator | /v1/messages |
| DeepL | DeepLTranslator | /v2/translate |
| Google Translate | GoogleTranslator | /language/translate/v2 |
| Gemini | GeminiTranslator | v1beta/models/{model}:generateContent |
§Quick start (library)
Translate an MKV file in-place:
use std::sync::Arc;
use psyche_subtitle_toolkit::{translate_mkv, TranslateMkvOptions, OllamaTranslator, Translator};
let translator: Arc<dyn Translator> = Arc::new(OllamaTranslator::new("llama3.1")?);
translate_mkv(
TranslateMkvOptions {
input: "/media/anime/episode.mkv".into(),
target_language: "pt-BR".into(),
track_id: None,
keep_temp: false,
dry_run: false,
resume: false,
max_concurrent: 1,
},
translator,
).await?;Translate ASS content directly (no MKV I/O):
use std::sync::Arc;
use psyche_subtitle_toolkit::{translate_ass, AssSubtitle, OllamaTranslator, Translator};
let ass = AssSubtitle::parse(&std::fs::read_to_string("source.ass")?)?;
let translator: Arc<dyn Translator> = Arc::new(OllamaTranslator::new("llama3.1")?);
let translated = translate_ass(ass, "pt-BR", 1, translator).await?;
std::fs::write("translated.ass", translated.render())?;Implement a custom provider:
use async_trait::async_trait;
use psyche_subtitle_toolkit::{Translator, TranslationRequest, Result};
struct MyTranslator;
#[async_trait]
impl Translator for MyTranslator {
async fn translate(&self, request: TranslationRequest<'_>) -> Result<String> {
// Your translation logic here.
// request.source_text is numbered: "<1> hello\n<2> world"
// Return translated text in the same format.
Ok(request.source_text.to_string())
}
}§Pipeline overview
- Inspect —
mkvmerge -Jidentifies tracks, selects the ASS subtitle - Extract —
mkvextract trackspulls the ASS file to a temp directory - Parse — ASS parser reads dialogue lines, preserving headers and styles
- Strip tags — Override tags (
{\pos(...)},{\an7}) removed and stored - Chunk — Cues split into 200-line batches
- Translate — Each chunk sent to the provider (concurrent if
max_concurrent > 1) - Retry — Failed chunks retried up to 3 times with exponential backoff
- Apply — Translated text mapped back to cues by ID
- Reinject tags — Original override tags prepended back
- Mux —
mkvmergereplaces the original subtitle track in-place
Re-exports§
pub use error::Result;pub use error::SubtitleToolkitError;pub use media::mkv::MkvInfo;pub use media::mkv::MkvTrack;pub use media::mkv::MkvTrackProperties;pub use media::mkv::SubtitleFormat;pub use media::mkv::inspect_mkv;pub use media::mkv::select_ass_track;pub use media::mkv::select_subtitle_track;pub use pipeline::TranslateMkvOptions;pub use pipeline::dry_run_summary;pub use pipeline::translate_ass;pub use pipeline::translate_mkv;pub use pipeline::translate_srt;pub use pipeline::translate_vtt;pub use subtitles::ass::AssSubtitle;pub use subtitles::srt::SrtSubtitle;pub use subtitles::vtt::VttSubtitle;pub use subtitles::model::SubtitleCue;pub use subtitles::model::SubtitleDocument;pub use subtitles::structured::apply_translation;pub use subtitles::structured::chunk_document;pub use subtitles::structured::chunk_document_by_lines;pub use subtitles::structured::parse_numbered_text;pub use subtitles::structured::to_numbered_text;pub use translation::anthropic::AnthropicTranslator;pub use translation::deepl::DeepLTranslator;pub use translation::gemini::GeminiTranslator;pub use translation::google::GoogleTranslator;pub use translation::ollama::OllamaTranslator;pub use translation::openai::OpenAiTranslator;pub use translation::openrouter::OpenRouterTranslator;pub use translation::TranslationRequest;pub use translation::Translator;
Modules§
- error
- Error types for the subtitle toolkit.
- media
- MKV container inspection and manipulation (mkvmerge/mkvextract wrappers).
- pipeline
- Translation pipeline: MKV full-pipeline and subtitle-only translation.
- subtitles
- Subtitle parsing, chunking, and tag manipulation.
- translation
- Pluggable translation providers and the
Translatortrait.