{"id":43822761,"url":"https://github.com/ellite/anchor-sub-sync","last_synced_at":"2026-02-24T23:01:44.362Z","repository":{"id":336754595,"uuid":"1146693067","full_name":"ellite/anchor-sub-sync","owner":"ellite","description":"Anchor: A universal, hardware-accelerated CLI tool for subtitle synchronization (Whisper) and context-aware translation (NLLB)","archived":false,"fork":false,"pushed_at":"2026-02-22T14:36:38.000Z","size":547,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-22T19:34:27.711Z","etag":null,"topics":["ai","audio-transcription","automation","cli","cuda","nllb","python","pytorch","srt","subtitle-sync","subtitle-translation","subtitles","synchronization","translation","whisper"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ellite.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-31T14:23:17.000Z","updated_at":"2026-02-22T14:36:41.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ellite/anchor-sub-sync","commit_stats":null,"previous_names":["ellite/anchor-sub-sync"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/ellite/anchor-sub-sync","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ellite%2Fanchor-sub-sync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ellite%2Fanchor-sub-sync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ellite%2Fanchor-sub-sync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ellite%2Fanchor-sub-sync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ellite","download_url":"https://codeload.github.com/ellite/anchor-sub-sync/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ellite%2Fanchor-sub-sync/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29804136,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-24T22:43:48.403Z","status":"ssl_error","status_checked_at":"2026-02-24T22:43:18.536Z","response_time":75,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","audio-transcription","automation","cli","cuda","nllb","python","pytorch","srt","subtitle-sync","subtitle-translation","subtitles","synchronization","translation","whisper"],"created_at":"2026-02-06T02:07:29.928Z","updated_at":"2026-02-24T23:01:44.356Z","avatar_url":"https://github.com/ellite.png","language":"Python","funding_links":["https://github.com/sponsors/ellite"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cpicture\u003e\n    \u003cimg src=\"https://github.com/ellite/anchor-sub-sync/raw/main/resources/anchorlogo.png\" alt=\"Anchor logo\" width=\"160\"\n  \u003c/picture\u003e\n\n  \u003cp\u003eAnchor Sub Sync\u003c/p\u003e\n\n  [![Stars](https://img.shields.io/github/stars/ellite/anchor-sub-sync?style=flat-square)](https://github.com/ellite/anchor-sub-sync)\n  [![Downloads](https://img.shields.io/pepy/dt/anchor-sub-sync?style=flat-square\u0026color=blue)](https://pepy.tech/project/anchor-sub-sync)\n  [![GitHub contributors](https://img.shields.io/github/contributors/ellite/anchor-sub-sync?style=flat-square)](https://github.com/ellite/anchor-sub-sync/graphs/contributors)\n  [![GitHub Sponsors](https://img.shields.io/github/sponsors/ellite?style=flat-square)](https://github.com/sponsors/ellite)\n\u003c/div\u003e\n\n# ⚓ Anchor Sub Sync\n\n**Anchor** is a GPU-accelerated tool that automatically synchronizes subtitle files (.srt, .ass) to video files using audio alignment. It uses OpenAI's Whisper (via WhisperX) to listen to the video track, applies some alignment techniques and perfectly align every subtitle line.\n\n\n⚡ Core Capabilities\n\n- 🔊 **Audio Sync**: Auto-align subtitles to video using Whisper (no reference text needed).\n- 📍 **Point Sync**: Fix linear drift by matching distinct lines against a reference subtitle.\n- 🌐 **Translation**: Context-aware translation using NLLB, with dual-speaker preservation and auto-formatting.\n- 📝 **Transcriptions**: Generate subtitles directly from the video.\n- 📦 **Container Tasks**: Extract, Embed, or Strip subtitles from media\n- 🔥 **Burn-in**: Permanently burn subtitles into video\n- 🧽 **Clean \u0026 Fix**:  Repair and clean subtitle files\n\n## How It Works - Under the Hood\n\nA short summary: Anchor uses WhisperX plus a multi-stage elastic alignment pipeline (phoneme alignment, global fuzzy alignment, rolling-window drift correction, and timeline interpolation) to produce millisecond-accurate subtitle sync.\n\n- **Transcription:** WhisperX for high-quality transcripts + forced alignment.\n- **Phoneme Alignment:** Maps audio to phonemes for word-level timing.\n- **Global Alignment:** Fuzzy matching to find anchors between script and audio.\n- **Drift Correction:** Rolling-window filter to correct long-term drift.\n- **Cleanup:** Interpolation + overlap-fixer (\"Zipper\") for clean subtitles.\n\n\u003cdetails\u003e\n\u003csummary\u003eFull explanation (expand)\u003c/summary\u003e\n\nMany syncing tools rely on simple waveform matching or standard speech-to-text, which often results in \"drift\" (where subtitles slowly get out of sync) or fails when the actor changes a line.\n\nThe engine combines WhisperX with a multi-stage Elastic Alignment Algorithm. Here is what is happening during those processing steps and why it matters.\n\n1. Why WhisperX instead of Faster-Whisper?\nStandard faster-whisper is incredible at transcription, but it often \"hallucinates\" timestamps. It guesses roughly when a sentence started, often missing by 0.5 to 1.0 seconds.\n\nWhisperX adds a crucial post-processing step called Forced Alignment.\n\nThe Difference: Instead of just listening for words, it maps the audio to specific Phonemes (the distinct units of sound, like the \"t\" in \"cat\").\n\nThe Result: The tool doesn't just know what was said; it identifies word-level timing precision down to the millisecond. This provides the bedrock for perfect sync.\n\n2. The Alignment Logic (Step-by-Step)\nOnce transcription is complete, the custom syncing pipeline merges the provided script with the actual audio.\n\n📏 Aligning Phonemes: The system maps the raw audio sounds to the text characters. This ensures that even if the actor speaks quickly or mumbles, the tool catches the exact moment the sound occurs.\n\n📐 Global Alignment (e.g., 6153 vs 6063 words): Scripts rarely match the final audio perfectly. Actors add-lib, scenes get cut, or words are repeated.\n\nStandard tools fail here because they look for a 1:1 match.\n\nThis tool performs a \"fuzzy match\" global alignment, mathematically calculating the best fit between the two datasets even when the word counts differ.\n\n🔍 Rolling Window Drift Filter: Over a long video, audio timing can \"drift\" (often due to frame rate conversions like 23.976fps vs 24fps). This step analyzes the timeline in moving segments to detect and correct gradual desynchronization before it becomes noticeable to the viewer.\n\n⚓️ Valid Anchors (e.g., 618 Anchors): The system identifies \"Anchors\"-points of absolute certainty where the audio and text match perfectly with high confidence.\n\nRejection: It automatically discards \"Outliers\" (matches that seem statistically unlikely or erroneous), ensuring the timeline is pinned down only by high-quality data.\n\n🔨 Reconstructing Timeline (Interpolation): Using the Anchors as fixed distinct points, the system mathematically \"stretches\" or \"compresses\" the text between them. This ensures that the dialogue between the perfect matches flows naturally and stays in sync.\n\n🧹 Running The Zipper (Overlap Cleanup): Subtitle overlaps are messy and hard to read. \"The Zipper\" is a final polish pass that detects when two subtitle events collide. It dynamically adjusts the start/end times to ensure one line finishes exactly as the next one begins, resolving conflicts automatically.\n\nWhy use this over other tools?\nMost tools treat subtitles as a static block of text. This system treats them as a dynamic, elastic timeline. By using Phoneme-level anchoring combined with Drift Correction, the tool can sync messy, imperfect scripts to audio with a precision that manual matching simply cannot achieve.\n\n\u003c/details\u003e\n\n## 🌍 Automatic Cross-Language Sync\n\nAnchor 1.1+ can now synchronize subtitles even when they are in a different language than the audio (for example, English audio with Portuguese subtitles). This is done by creating a temporary \"ghost\" translation of your subtitles into the audio language, syncing that translation to the audio, and then applying the improved timestamps back to your original file.\n\n\u003cdetails\u003e\n\u003csummary\u003eHow it works (expand)\u003c/summary\u003e\n\n1. **Detection:** Anchor detects a mismatch between the audio language and the subtitle file language (e.g., Audio: EN, Subtitle: PT).\n\n2. **Translation:** Anchor uses a fast neural translation model (NLLB via CTranslate2) to create a temporary translated subtitle file in the audio language.\n\n3. **Sync:** The temporary \"ghost\" translation is synchronized against the audio track using the normal alignment pipeline.\n\n4. **Restoration:** The accurate timestamps are transferred back to your original subtitle file, preserving the original text while fixing timing.\n\nThe result is your original subtitle text, timed accurately to the foreign-language audio.\n\n\u003c/details\u003e\n\n### 🧠 Translation Models\n\nAnchor uses NLLB-200 (No Language Left Behind) via the `ctranslate2` engine for high speed and low memory usage. You can override the automatic choice with the `-t` / `--translation-model` flag.\n\nNote: The first time you use a translation model it will be downloaded automatically (approx. 1GB - 3.5GB depending on model). Models are cached locally for reuse.\n\nSupported Translation Models\n----------------------------\n\nThe following pre-built CTranslate2 models are supported and can be passed directly to `-t`:\n\n- `JustFrederik/nllb-200-distilled-600M-ct2-int8`\n- `OpenNMT/nllb-200-distilled-1.3B-ct2-int8`\n- `OpenNMT/nllb-200-3.3B-ct2-int8`\n\nFor convenience, Anchor also supports shorthand names that map to reasonable defaults:\n\n| Shorthand | Model |\n| --------- | ----- |\n| `small` | `JustFrederik/nllb-200-distilled-600M-ct2-int8` |\n| `medium` | `OpenNMT/nllb-200-distilled-1.3B-ct2-int8` |\n| `large` | `OpenNMT/nllb-200-3.3B-ct2-int8` |\n\nExample usages:\n\n```bash\nanchor -t small\nanchor --translation-model OpenNMT/nllb-200-1.3B-ct2-int8\n```\n\nAnd ensure you have enough disk space for model downloads.\n\n## ⚡ Performance Test with model large-v3\n\n- GPU (NVIDIA RTX 2000E): synced a 44-minute episode in ~82 seconds.\n- CPU (Intel i5-12600H): synced the same 44-minute episode in ~16 minutes.\n- Notes: Results vary by hardware, drivers, and model precision. Haven't tested other devices extensively; it should work on AMD and Intel GPUs as well.\n\n### ⚡ Performance Benchmarks\n\n*Tests performed on a 44-minute video file (English). With music and gun fire scenes.* **Hardware:** NVIDIA RTX 2000E Ada Generation (16 GB)\n\n| Model | Translation | Time | Speed | Anchors Found | User Score | Notes |\n| :--- | :---: | :---: | :---: | :---: | :--- | :--- |\n| **Large-v3** | 3.3B | 98s | ~27x | 514 | 10 / 10 | Translation of 669 lines added 16s. |\n| **Large-v3** | N/A | 82s | ~32x | 600 | 10 / 10 | The gold standard for accuracy, but slower. |\n| **Medium.en** | N/A | 62s | ~42x | 598 | 10 / 10 | Best Balance. Perfect sync, identical to Large-v3 but 25% faster. |\n| **Small.en** | N/A | 44s | ~60x | 603 | 9.9 / 10 | Fastest usable. Twice as fast as Large-v3. Minor sync drift (\u003c0.1s). |\n| **Base.en** | N/A | 35s | ~75x | 585 | 9.7 / 10 | Very fast, but prone to drift on non-speech sounds (e.g., gunshots). |\n\n### 📉 CPU Performance Benchmarks\n\n*Tests performed on a 44-minute video file (English).* **Hardware:** 12th Gen Intel Core i5-12600H\n\n| Model | Translation | Time | Speed | Anchors Found | User Score | Notes |\n| :--- | :---: | :---: | :---: | :---: | :---: | :--- |\n| **Large-v3** | N/A | ~16 min | ~2.7x | 599 | 10 / 10 | *Baseline.* Too slow for daily use on CPU. |\n| **Medium.en** |\t1.3B | 12.5 min | ~3.5x | 518 | 10 / 10 | Translation of 669 lines added ~60s. |\n| **Medium.en** | N/A | 11.5 min | ~3.8x | 595 | 10 / 10 | Accurate, but the extra 6-minute wait didn't add much value. |\n| **Small.en** | N/A | 5.7 min | ~7.6x | 601 | 10 / 10 | 2x faster than Medium with *better* anchor detection. |\n| **Base.en** | N/A | 4.1 min | ~11x | 579 | 9.7 / 10 | Decent, but not significantly better than Tiny to justify the extra time. |\n| **Tiny.en** | N/A | 3.7 min | ~12x | 572 | 9.5 / 10 | Incredibly fast. Good overall sync, but slightly less precise start times (ms delay). |\n\n## 🚀 Requirements\n\n* **OS:** Linux | Windows | MacOS\n* **GPU:** GPU Recommended, CPU only is possible (but slower)\n* **Python:** 3.10 or 3.11\n* **ffmpeg / ffprobe:** required for audio probing, metadata language detection, and audio extraction\n\n## 📦 Installation\n\n### 1. Install Anchor\nInstall the tool directly from the repository.\n\n```bash\npip install anchor-sub-sync\n```\n\n**⚠️ Important:** Because this tool relies on hardware acceleration, standard `pip install` often pulls the wrong drivers (CPU versions). If it does not work out of the box, please follow these steps in order.\n\n### 2. Install PyTorch (choose per hardware)\n\nBefore installing PyTorch, run a few quick checks to detect your hardware:\n\n```bash\n# NVIDIA / CUDA\nnvidia-smi || true\npython3 -c \"import torch; print('cuda', torch.cuda.is_available(), getattr(torch.cuda, 'get_device_name', lambda i: '')(0))\"\n\n# Apple Silicon (MPS)\npython3 -c \"import torch; print('mps', torch.backends.mps.is_available())\"\n```\n\nThen install the appropriate build:\n\n- NVIDIA GPU with CUDA drivers installed:\n\n```bash\npip install torch torchaudio torchvision --index-url https://download.pytorch.org/whl/cu121\n```\n\n- Apple Silicon (MPS) - follow PyTorch MPS instructions (example):\n\n```bash\npip install torch torchaudio torchvision --index-url https://download.pytorch.org/whl/mps\n```\n\n- AMD / ROCm: follow the official PyTorch ROCm install instructions for your distribution (see https://pytorch.org).\n\n- CPU-only systems:\n\n```bash\npip install torch torchaudio torchvision\n```\n\nNotes:\n- Only install the CUDA wheel if you have an NVIDIA GPU and matching drivers; installing a GPU wheel on a CPU-only system can produce subtle runtime errors.\n- If you encounter the `torchvision::nms` error, force-reinstall matching PyTorch/TorchVision wheels (see Troubleshooting below).\n\n### 3. Finalize Dependencies (Critical)\nSome libraries may downgrade during installation. Run this command to ensure the GPU translation engine is up to date and compatible with your drivers:\n\n```bash\npip install --upgrade ctranslate2\n```\n\n## 🛠️ Troubleshooting\n\n### \"Aborted\" (Core Dump) on Launch\nIf the tool crashes instantly without an error message, it is a library path issue common on Linux. Run this command before starting `anchor`:\n\n```bash\nexport LD_LIBRARY_PATH=$(python3 -c 'import os; import nvidia.cublas.lib; import nvidia.cudnn.lib; print(os.path.dirname(nvidia.cublas.lib.__file__) + \":\" + os.path.dirname(nvidia.cudnn.lib.__file__))')\n```\n*(Tip: Add the line above to your `~/.bashrc` file to make it permanent.)*\n\n### \"Operator torchvision::nms does not exist\"\nThis means you have a mismatch between PyTorch and TorchVision (usually one is CPU and one is GPU). Fix it by forcing a reinstall:\n\n```bash\npip install --force-reinstall torch torchvision torchaudio --index-url [https://download.pytorch.org/whl/cu121](https://download.pytorch.org/whl/cu121)\n```\n\n### \"Numpy is not compatible\"\nIf you see errors related to `numpy` (e.g., `module 'numpy' has no attribute...`), downgrade it to a stable version:\n\n```bash\npip install \"numpy\u003c2.0\" \"pandas\u003c2.0\"\n```\n\n### _curses missing on windows ###\n```bash\npip install windows-curses\n```\n\n## 🎬 Usage\n\nNavigate to the folder containing your subtitles and video files, then run:\n\n```bash\nanchor\n```\n\nCommand line options\n--------------------\n\nYou can override the automatic hardware detection or control specific settings using flags:\n\n| Option | Alias | Description |\n| ------ | ----- | ----------- |\n| --audio-model | -a | Force a specific Whisper model (e.g., tiny, medium, large-v3-turbo). |\n| --batch-size | -b | Manually set the batch size (e.g., 8, 16). Useful for optimizing VRAM usage. |\n| --translation-model | -t | Force a specific translation model (overrides automatic selection). |\n| --subtitle | -s | Runs unattended sync on a single subtitle file (provide path to .srt, .ass, etc.) |\n| --reference | -r | For unattended sync, provide reference subtitle file path for point sync |\n| --video | -v | For unattended sync, provide path to the video file if the script fails to auto-match |\n| --overwrite | -o | Will overwrite the synced subtitle instead of saving it as file.synced.srt |\n| --help | -h  | Show the help message and exit. |\n| --language | -l | For unattended mode, provide the target language code (e.g. 'en', 'pt', 'fr') for translation |\n\nExamples\n--------\n\nForce a specific model:\n\n```bash\nanchor --audio-model large-v3-turbo\n```\n\nForce batch size (to prevent crashes or increase speed):\n\n```bash\nanchor --batch-size 8\n```\n\nCombine flags:\n\n```bash\nanchor -a medium -b 16\n```\n\nRun unattended sync:\n\n```bash\nanchor -s A.3.Minutes.Example.Video.en.srt -v A.3.Minutes.Example.Video.mkv \n```\n\nRun unattended point sync:\n\n```bash\nanchor -s A.3.Minutes.Example.Video.en.srt -r A.3.Minutes.Example.Video.pt.srt \n```\nRun unattended translation:\n\n```bash\nanchor -s A.3.Minutes.Example.Video.en.srt -l pt \n```\n\n## ⚙️ Development\n\nTo modify the code locally:\n\n```bash\ngit clone [https://github.com/ellite/anchor-sub-sync.git](https://github.com/ellite/anchor-sub-sync.git)\ncd anchor-sub-sync\npip install -e .\n```\n## 🖼️ Screenshots \n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./resources/screenshot.png\" alt=\"Anchor screenshot\" width=\"800\" /\u003e\n\u003c/p\u003e\n\n---\n\n## ❌ Uninstallation\n\nIf you installed Anchor via `pip` (GitHub), uninstall with:\n\n```bash\npip uninstall anchor-sub-sync\n```\n\nIf you used an editable install during development (`pip install -e .`), also remove the cloned repository directory and any virtual environment you created:\n\n```bash\n# from the project directory\ndeactivate  # if in a venv\nrm -rf venv/  # or the name of your virtualenv\nrm -rf anchor-sub-sync/  # delete the cloned repo if desired\n```\n\nTo remove model caches or large downloaded files, delete the relevant cache directories (varies by model/backends), for example:\n\n```bash\nrm -rf ~/.cache/whisper ~/.cache/whisperx\n```\n\n## ⚓ Links\n- [GitHub](https://github.com/ellite/anchor-sub-sync)\n- [PyPI](https://pypi.org/project/anchor-sub-sync/)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fellite%2Fanchor-sub-sync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fellite%2Fanchor-sub-sync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fellite%2Fanchor-sub-sync/lists"}