A Python tool for refining bilingual (currently English-Chinese) ASS subtitles using Large Language Models (LLMs). With a basic level memory support, it allows you to resume progress via the checkpoint file. This implementation is especially suitable when you're looking for translation quality over batch speed.
Check /experiment and find more!
- Smart ASS Parsing: Parses
.asssubtitle files and matches English-Chinese pairs by timestamp - Intelligent Chunking: Splits subtitles into chunks that fit within LLM token limits
- Bilingual Refinement:
- English: Fixes capitalization, spacing, and punctuation only (preserves meaning)
- Chinese: Improves translation quality, naturalness, and consistency
- Global Memory: Maintains a terminology glossary and style notes across chunks, with a resume and checkpoint to allow you to continue work easily.
- ASS Tag Preservation: Keeps all formatting tags (e.g.,
{\i1},{\b1},\N) intact - Token Tracking: Monitors API usage and estimates costs in real-time
- Robust Error Handling: Automatic retries with exponential backoff
- Progress Reporting: Real-time progress updates during processing
(no longer suggested as it's the old version) - Click to expand
# 1. Create virtual environment
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# 2. Install dependencies
pip install -r requirements.txt
# 3. Set API key (or edit config.py)
export OPENAI_API_KEY="your-api-key-here"
# 4. Process subtitles (not suggested)
python main.py example_input.ass output.ass
# 5. Test with sample (first 10 pairs)(not suggested)
python main.py example_input.ass output.ass --dry-runWe recommended to use the excutable file in /experiment directory:
# 1. Create virtual environment
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# 2. Install dependencies
pip install -r requirements.txt
# 3. (IMPORTANT) Set you key in a file, and set its path in ./experiment/config.yaml
echo "YOUR_KEY" > key
# 4. Process subtitles (not suggested)
python ./experiment/main_sdk.py example_input.ass output.ass \
--streaming --pairs-per-chunk 105 \
--checkpoint --per-block-update -vvvThe ASS subtitle pairs are detected accorading to example_input.ass file, so you need to follow this format.
- Python 3.10 or higher
- OpenAI API key (or compatible API endpoint)
-
Clone or download this repository
-
Create a virtual environment (recommended):
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate- Install dependencies:
pip install -r requirements.txt-
Configure API key:
Option A - Environment variable (recommended):
export OPENAI_API_KEY="sk-proj-..."
Option B - Edit config.py:
api_key: str = "sk-proj-..."
-
Test the installation:
python main.py --test-connection input.ass output.asspython main.py input.ass output.assThis will:
- Parse the input
.assfile - Extract English-Chinese subtitle pairs
- Process them through the LLM for refinement
- Write the refined subtitles to
output.ass - Display token usage and cost estimation
usage: main.py [-h] [--model MODEL] [--dry-run] [--max-chunks MAX_CHUNKS]
[--memory-limit MEMORY_LIMIT] [--pairs-per-chunk PAIRS_PER_CHUNK]
[-v] [--stats STATS] [--test-connection]
input output
positional arguments:
input Input .ass subtitle file
output Output .ass subtitle file
optional arguments:
-h, --help Show this help message and exit
--model MODEL Model name (default: gpt-5.1)
--dry-run Process only first 10 pairs for testing
--max-chunks N Process only first N chunks
--memory-limit N Memory token limit (default: 2000)
--pairs-per-chunk N Number of subtitle pairs per chunk (overrides token-based chunking)
-v, --verbose Enable verbose output with timing and preview
--stats STATS Stats refresh interval in seconds for verbose mode (default: 1.0)
--test-connection Test API connection and exit
# 1. Basic processing (token-based chunking)
python main.py input.ass output.ass
# 2. Quick test with sample data (recommended for first use)
python main.py input.ass output.ass --dry-run
# 3. Process with fixed chunk size (50 pairs per chunk)
python main.py input.ass output.ass --pairs-per-chunk 50
# 4. Process only first 3 chunks
python main.py input.ass output.ass --max-chunks 3
# 5. Combine chunk size with max chunks (30 pairs per chunk, max 2 chunks)
python main.py input.ass output.ass --pairs-per-chunk 30 --max-chunks 2
# 6. Use a different model
python main.py input.ass output.ass --model gpt-4o
# 7. Increase memory limit for better context
python main.py input.ass output.ass --memory-limit 3000
# 8. Test API connection before processing
python main.py input.ass output.ass --test-connection
# 9. Enable verbose mode with timing and response preview
python main.py input.ass output.ass -v
# 10. Verbose mode with custom stats interval
python main.py input.ass output.ass -v --stats 0.5chmod +x example_usage.sh
./example_usage.shsubretrans/
├── main.py # CLI entry point and workflow orchestration
├── config.py # Configuration settings (API, tokens, pricing)
├── ass_parser.py # ASS file parsing and generation
├── pairs.py # SubtitlePair data structure
├── chunker.py # Smart chunk splitting with token limits
├── llm_client.py # OpenAI API client with retry logic
├── memory.py # Global memory management
├── prompts.py # System and user prompt templates
├── stats.py # Token usage statistics and cost tracking
├── utils.py # Utility functions (token estimation, etc.)
├── requirements.txt # Python dependencies
├── README.md # This file
├── example_usage.sh # Example usage script
├── IMPLEMENTATION_SUMMARY.md # Detailed implementation notes
└── venv/ # Virtual environment (created during setup)
-
Parse ASS File
- Reads
.assfile with UTF-8-sig encoding - Preserves header section ([Script Info], [V4+ Styles])
- Extracts all Dialogue lines from [Events] section
- Reads
-
Build Subtitle Pairs
- Matches English and Chinese lines by timestamp
- Identifies lines by style name (e.g., "English3", "Chinese3")
- Preserves all metadata (timing, style, margins, effects)
-
Split into Chunks
- Two chunking strategies available:
- Token-based (default): Uses tiktoken to fit chunks within context window
- Pair-based (with
--pairs-per-chunk): Fixed number of pairs per chunk
- Accounts for system prompt and memory overhead
- Two chunking strategies available:
-
Process Each Chunk
- Builds system prompt with refinement rules + global memory
- Sends subtitle pairs as JSON to LLM
- Parses and validates LLM response
- Updates global memory with new terminology
-
Memory Management
- Extracts proper nouns and terminology
- Maintains cross-chunk context
- Automatically compresses if memory exceeds limit
-
Generate Output
- Applies corrections back to original structure
- Preserves all ASS formatting and tags
- Writes complete
.assfile with refined subtitles
The tool supports two chunking strategies:
- How it works: Automatically calculates optimal chunk size based on token limits
- Advantages: Maximizes context window usage, reduces API calls
- Best for: Most use cases, especially with varying subtitle lengths
- Usage: Default behavior (no flag needed)
python main.py input.ass output.ass- How it works: Splits subtitles into fixed-size chunks by pair count
- Advantages: Predictable chunk sizes, easier cost estimation
- Best for: Consistent processing, testing, batch operations
- Usage: Specify with
--pairs-per-chunk N
# Process 50 pairs at a time
python main.py input.ass output.ass --pairs-per-chunk 50
# Smaller chunks for testing
python main.py input.ass output.ass --pairs-per-chunk 10Tip: Combine with --max-chunks to limit processing:
# Process first 100 pairs only (50 pairs/chunk × 2 chunks)
python main.py input.ass output.ass --pairs-per-chunk 50 --max-chunks 2The tool supports verbose mode for detailed progress tracking:
# Basic verbose mode (timing + preview)
python main.py input.ass output.ass -v
# Very verbose (-vv) dumps full API responses after each chunk
python main.py input.ass output.ass -vv
# Ultra verbose (-vvv) also prints the full system prompt/memory sent to the model
python main.py input.ass output.ass -vvv
# Verbose with custom stats interval
python main.py input.ass output.ass -v --stats 0.5-
Chunk Processing Time: Shows elapsed time for each chunk
- Example:
Time: 16.51s
- Example:
-
Response Preview + Reasoning Tokens: Real-time preview of LLM output
- Line 1-2: First two lines of returned subtitle pairs (JSON flattened to plain-text)
- Line 3: Reasoning tokens consumed (from API usage data)
-
Token Statistics: Standard token usage per chunk
-
Full API Response (optional): Use
-vvto print the entire raw API response after each chunk (useful for debugging JSON issues) -
System Prompt & Memory (optional): Use
-vvvto print the exact system prompt (including memory) sent to the model for each chunk
Example Verbose Output:
Processing chunk 1/2 (30 pairs)...
[Chunk 1/2] (50.0% complete)
Tokens used: 3,092 (prompt: 1,726, completion: 1,366)
Time: 16.51s
Response: [
{
Reasoning tokens: 8
When to use:
- Debugging processing issues
- Monitoring long-running jobs
- Analyzing response patterns
- Performance optimization
The tool applies minimal changes to English subtitles:
-
✅ Fix capitalization: First letter of sentences capitalized
- Before:
"tonight, on JAG..." - After:
"Tonight, on JAG..."
- Before:
-
✅ Fix spacing: Proper spacing around punctuation
- Before:
"Hello,world" - After:
"Hello, world"
- Before:
-
✅ Fix ending punctuation: Add periods to complete sentences
- Before:
"Good evening" - After:
"Good evening."
- Before:
-
❌ Do NOT change: Words, meanings, or phrasing
-
✅ Preserve: All ASS tags (
{\i1},{\b1},\N, etc.)
The tool applies comprehensive improvements to Chinese subtitles:
-
✅ Translation quality: Improve accuracy and clarity
- Before:
"军法署" - After:
"《JAG军法官》节目中"
- Before:
-
✅ Natural language: Make text more conversational
- Before:
"报告" - After:
"报道"
- Before:
-
✅ Punctuation: Add proper Chinese punctuation (。、!?等)
- Before:
"晚上好,我是诺曼·德拉波特" - After:
"晚上好,我是诺曼·德拉波特。"
- Before:
-
✅ Consistency: Maintain terminology and style across chunks
-
✅ Awkward phrasing: Fix unnatural expressions
-
❌ Do NOT change: ASS formatting tags
Tested with first 152 subtitle pairs from JAG.S04E08.zh-cn.ass:
Input: 152 pairs (304 dialogue lines)
Model: GPT-5.1
Processing: ~30 seconds
Chunks: 1 chunk
Tokens used: 12,993 total
- Prompt: 6,604 tokens
- Completion: 6,389 tokens
Estimated cost: $0.58 USD
Success rate: 100%
- Average tokens per pair: ~85 tokens
- Cost per pair: ~$0.0038 USD
- Processing speed: ~5 pairs/second
- Estimated cost for 1000 pairs: ~$3.80 USD
English refinements:
- Capitalization fixes: ~30% of lines
- Punctuation additions: ~40% of lines
- Spacing fixes: ~5% of lines
Chinese refinements:
- Translation improvements: ~20% of lines
- Punctuation additions: ~90% of lines
- Natural phrasing: ~15% of lines
- All ASS tags preserved: 100%
The system prompt is now generated from a single markdown template file (main_prompt.md):
- Template Structure: The template uses markdown sections (
### 1. English Subtitle Rules, etc.) - Dynamic Injection: The
### 4. User Terminology (Authoritative Glossary)section is dynamically updated with:- Template glossary entries (parsed from the file)
- Runtime
GlobalMemory.user_glossaryentries (merged, runtime takes precedence) - Learned terminology (appended as "Learned Terminology (Supplement)")
- Automatic Renumbering: All sections are renumbered automatically
### 1. English Subtitle Rules
### 2. Chinese Subtitle Rules
### 3. Context & Specific Handling
### 4. User Terminology (Authoritative Glossary) ← Dynamic injection point
### 5. Input/Output Format & Constraint
### 6. Few-Shot Examples- Single source of truth - All rules in one markdown file
- Easy customization - Edit markdown without code changes
- Dynamic terminology - Automatic glossary injection from GlobalMemory
- Backward compatible - Falls back to legacy prompt building if no config provided
Edit config.py to customize:
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class MainModelSettings:
name: str = "gpt-5.1"
max_output_tokens: int = 12000
reasoning_effort: str = "medium"
temperature: float = 1.0 # GPT-5.* has no temperature setting
@dataclass
class TerminologyModelSettings:
name: str = "gpt-4o-mini"
max_output_tokens: int = 1500
temperature: float = 0.3 # Lower temperature keeps glossary extraction stable
@dataclass
class Config:
api_key: str = ""
api_base_url: str = "https://api.openai.com/v1"
max_context_tokens: int = 128000
memory_token_limit: int = 2000
chunk_token_soft_limit: int = 100000
pairs_per_chunk: Optional[int] = None
api_timeout: int = 120
verbose: bool = False
very_verbose: bool = False
debug_prompts: bool = False
stats_interval: float = 1.0
dry_run: bool = False
max_chunks: Optional[int] = None
price_per_1k_prompt_tokens: float = 0.03
price_per_1k_completion_tokens: float = 0.06
glossary_max_entries: int = 100
glossary_policy: str = "lock"
user_prompt_path: str = "main_prompt.md"
terminology_min_confidence: float = 0.6
main_model: MainModelSettings = field(default_factory=MainModelSettings)
terminology_model: TerminologyModelSettings = field(default_factory=TerminologyModelSettings)- Reasoning effort: Adjust
config.main_model.reasoning_effortto hint GPT-5.1's reasoning depth ("none","low","medium","high"). - Temperature: Tune
config.main_model.temperature(defaults to1.0) for the primary GPT-5 run, andconfig.terminology_model.temperaturefor the GPT-4o terminology extractor if you need stricter or looser extraction. - Glossary limit: Adjust
config.glossary_max_entries(default100) if you need to retain more/fewer global terminology entries in the prompt. - Glossary policy:
config.glossary_policy="lock"means learned terminology can only add new entries and will never override user-defined mappings from the user glossary. - User prompt file: Set
config.user_prompt_pathto point at the main prompt template file (defaultmain_prompt.md). This template serves as the complete system prompt with dynamic terminology injection. - Terminology confidence:
config.terminology_min_confidencecontrols both the GPT‑4o prompt threshold and local filtering of extracted terms.
To use a different OpenAI-compatible API:
# In config.py
api_base_url: str = "https://your-api-endpoint.com/v1"
@dataclass
class MainModelSettings:
name: str = "your-model-name"
...The tool includes robust error handling:
-
API Errors
- Automatic retry with exponential backoff (3 attempts)
- Wait times: 1s, 2s, 4s between retries
- Graceful failure with error messages
-
Chunk Processing Failures
- Failed chunks are skipped (not discarded)
- Processing continues with remaining chunks
- Original subtitles preserved for failed chunks
-
Memory Overflow
- Automatically compresses memory when limit exceeded
- LLM-based compression to preserve important terms
- Fallback to simple truncation if compression fails
-
JSON Parsing Errors
- Attempts to extract JSON from markdown code blocks
- Validates structure before processing
- Clear error messages for debugging
-
File I/O Errors
- Checks file existence before processing
- Validates UTF-8 encoding
- Creates output directory if needed
The tool provides real-time cost tracking:
==================================================
TOKEN USAGE REPORT
==================================================
Prompt tokens: 6,604
Completion tokens: 6,389
Total tokens: 12,993
--------------------------------------------------
Estimated cost: $ 0.5815 USD
==================================================
- Prompt tokens: $0.03 per 1K tokens
- Completion tokens: $0.06 per 1K tokens
Note: Actual costs may vary based on your OpenAI plan and model used. Update pricing in config.py for accurate estimates.
1. API Key Error
Configuration error: API key must be provided
Solution: Set OPENAI_API_KEY environment variable or edit config.py
2. Model Not Found
API request failed: model 'gpt-5.1' not found
Solution: Use --model gpt-4o or another available model
3. Token Limit Exceeded
API request failed: maximum context length exceeded
Solution: Reduce chunk_token_soft_limit in config.py
4. No Subtitle Pairs Found
Error: No subtitle pairs found
Solution: Ensure your .ass file has both English and Chinese dialogue lines with matching timestamps
5. Import Error
ModuleNotFoundError: No module named 'tiktoken'
Solution: Activate virtual environment and run pip install -r requirements.txt
For verbose output, modify main.py to add debug logging:
import logging
logging.basicConfig(level=logging.DEBUG)Dialogue: 1,0:00:02.56,0:00:04.00,Chinese3,NTP,0000,0000,0000,,今晚,在军法署...
Dialogue: -1,0:00:02.56,0:00:04.00,English3,NTP,0000,0000,0000,, Tonight, on JAG...
Dialogue: 1,0:00:02.56,0:00:04.00,Chinese3,NTP,0000,0000,0000,,今晚,在《JAG军法官》节目中...
Dialogue: -1,0:00:02.56,0:00:04.00,English3,NTP,0000,0000,0000,,Tonight, on JAG...
Changes made:
- English: Removed leading spaces, capitalized "Tonight"
- Chinese: Improved translation ("军法署" → "《JAG军法官》节目中")
- Format Support: Only supports
.asssubtitle format (not.srt,.vtt, etc.) - Language Pair: Designed for English-Chinese pairs only
- Timestamp Matching: Requires exact timestamp matches between English and Chinese lines
- LLM Quality: Output quality depends on the model used (GPT-5.1 recommended)
- Edge Cases: May not preserve all complex ASS formatting in rare cases
- Single File Processing: Processes one file at a time (no batch mode) Here are two polished versions—you can choose the tone you prefer:
- No compatibility with other API formats is guaranteed: There are plans to support OpenAI-compatible APIs (e.g., NewAPI).
Priorty:
- Global across eposides via series memory file/ Redis?
Potential features for future versions:
- Support for
.srtand.vttformats - Batch file processing
- Custom terminology dictionaries
- Diff report generation (showing all changes)
- GUI/Web interface
- Parallel chunk processing
- Quality scoring metrics
- Support for more language pairs
tiktoken>=0.5.1 # OpenAI's token counting library
requests>=2.31.0 # HTTP client for API calls
python-dotenv>=1.0.0 # Environment variable management
All dependencies are listed in requirements.txt.
This repository is licensed under the MIT License. The example subtitle is not part of the licensed content, and its copyright holder retains all rights.
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Make your changes with tests
- Submit a pull request
# Clone repository
git clone <repository-url>
cd subretrans
# Create virtual environment
python3 -m venv venv
source venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Run tests
python main.py test_input.ass test_output.ass --dry-runFor issues, questions, or suggestions:
- Open an issue on GitHub
- Check IMPLEMENTATION_SUMMARY.md for detailed technical documentation
- Review plan.md for design decisions
- Built using OpenAI's GPT models
- Uses
tiktokenfor accurate token counting - Follows ASS subtitle format specification
Version: 0.0.6 Last Updated: December 1, 2025 Status: Use at your own dangers