[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-82771":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":15,"forks30d":15,"starsTrendScore":19,"compositeScore":20,"rankGlobal":10,"rankLanguage":10,"license":21,"archived":22,"fork":22,"defaultBranch":23,"hasWiki":22,"hasPages":22,"topics":24,"createdAt":10,"pushedAt":10,"updatedAt":37,"readmeContent":38,"aiSummary":39,"trendingCount":15,"starSnapshotCount":15,"syncStatus":40,"lastSyncTime":41,"discoverSource":42},82771,"subforge","deusjin\u002Fsubforge","deusjin","Rust CLI for AI subtitle workflows: transcribe, segment, translate, evaluate, and burn or mux subtitles.","https:\u002F\u002Fgithub.com\u002Fdeusjin\u002Fsubforge#readme",null,"Rust",87,8,55,0,1,22,32,14,2.86,"MIT License",false,"main",[25,26,27,28,29,30,31,32,33,34,35,36],"cli","faster-whisper","ffmpeg","llm","openai","rust","speech-recognition","subtitle-translation","subtitles","translation","video-processing","whisper","2026-06-12 02:04:27","\u003Cdiv align=\"center\">\n  \u003Cimg src=\"assets\u002Flogo-compact.png\" alt=\"SubForge\" height=\"96\">\u003Cbr>\n  \u003Cstrong>Turn video subtitle production into a reproducible AI pipeline.\u003C\u002Fstrong>\u003Cbr>\n  \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fdeusjin\u002Fsubforge\u002Factions\u002Fworkflows\u002Fci.yml\">\u003Cimg alt=\"CI\" src=\"https:\u002F\u002Fgithub.com\u002Fdeusjin\u002Fsubforge\u002Factions\u002Fworkflows\u002Fci.yml\u002Fbadge.svg\">\u003C\u002Fa>\n  \u003Ca href=\"LICENSE\">\u003Cimg alt=\"License: MIT\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FLicense-MIT-blue.svg\">\u003C\u002Fa>\n  \u003Ca href=\"Cargo.toml\">\u003Cimg alt=\"Rust 1.88+\" src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Frust-1.88%2B-orange.svg\">\u003C\u002Fa>\u003Cbr>\n  \u003Ca href=\"README.md\">English\u003C\u002Fa> |\n  \u003Ca href=\"README.zh-CN.md\">简体中文\u003C\u002Fa>\n\u003C\u002Fdiv>\n\nSubForge is a Rust CLI for transcribing, segmenting, translating, evaluating,\nand muxing or burning subtitles into videos. It is built for people who process\nvideos repeatedly and do not want every project to become a pile of scripts,\ntemporary files, model paths, ffmpeg flags, and manual rework.\n\n```text\nvideo \u002F audio\n  -> speech recognition\n  -> subtitle segmentation\n  -> translation\n  -> quality estimation\n  -> hard-burned video \u002F soft subtitle track\n```\n\n\u003Cp align=\"center\">\n  \u003Cimg src=\"assets\u002Fresult.png\" alt=\"SubForge bilingual subtitle output preview\" width=\"900\">\n\u003C\u002Fp>\n\n## Why SubForge\n\nMost subtitle workflows are not one tool. They are a chain:\n\n- extract or transcribe audio\n- split text into readable subtitle cues\n- translate with enough context to keep terms stable\n- check low-quality translations\n- render subtitles into a video or mux them as a subtitle track\n- keep intermediate outputs, caches, models, and project memory organized\n\nSubForge makes that chain explicit, repeatable, and inspectable.\n\nIt is not a GUI editor. It is a CLI-first tool for local automation,\nbatch processing, video localization, course translation, and creator\nworkflows where reproducibility matters.\n\n## Highlights\n\n- Rust CLI with Linux, macOS, and Windows CI\n- Local `faster-whisper` transcription with CPU or CUDA support\n- SaT-based subtitle segmentation via an embedded Python sidecar\n- Google, Bing, and OpenAI-compatible LLM translation backends\n- Two LLM translation modes:\n  - chained translation for best context continuity\n  - wave-based concurrent translation for long videos\n- MAPS-style terminology extraction and project-level translation memory\n- GEMBA-MQM quality estimation with targeted low-score refinement\n- Hard subtitle burning and soft subtitle muxing through ffmpeg\n- GPU selection for faster-whisper and NVENC encoders\n- Model download, cache management, environment diagnostics, and config tools\n- Secret scanning in CI\n\n## Status\n\nSubForge is usable, but still early. The current release is `0.2.0`.\n\nThe core CLI, ffmpeg integration, cache handling, model management, and\nconfiguration flow are already in place. The next layer of work is better\nrelease automation, broader real-world end-to-end testing, and more polished\ndocumentation for recommended translation settings.\n\n## Installation\n\nSubForge currently builds from source. Prebuilt binaries are not published yet.\n\n### Requirements\n\n| Tool | Purpose |\n| --- | --- |\n| Rust 1.88+ | Build the `subforge` binary |\n| Python 3.9+ | Run faster-whisper, SaT, SubER, and related sidecars |\n| ffmpeg | Extract audio, burn subtitles, and mux subtitle tracks |\n\nInstall Rust:\n\n```bash\ncurl --proto '=https' --tlsv1.2 -sSf https:\u002F\u002Fsh.rustup.rs | sh -s -- -y --profile minimal # Linux \u002F macOS\n```\n\nWindows PowerShell:\n\n```powershell\n$env:RUSTUP_DIST_SERVER=\"https:\u002F\u002Frsproxy.cn\"\n$env:RUSTUP_UPDATE_ROOT=\"https:\u002F\u002Frsproxy.cn\u002Frustup\"\n\nwinget install --id Rustlang.Rustup --exact --force\n\nrustup set profile minimal\nrustup toolchain install stable\nrustup default stable\ncargo --version\n\nmkdir $env:USERPROFILE\\.cargo -Force\n@\"\n[source.crates-io]\nreplace-with = \"rsproxy\"\n\n[source.rsproxy]\nregistry = \"sparse+https:\u002F\u002Frsproxy.cn\u002Findex\u002F\"\n\"@ | Set-Content -Encoding UTF8 $env:USERPROFILE\\.cargo\\config.toml\n```\n\nInstall ffmpeg:\n\n```bash\nsudo apt install ffmpeg            # Debian \u002F Ubuntu\nsudo dnf install ffmpeg            # Fedora\nsudo pacman -S ffmpeg              # Arch\nbrew install ffmpeg                # macOS\nwinget install Gyan.FFmpeg         # Windows\n```\n\nInstall SubForge:\n\n```bash\ngit clone https:\u002F\u002Fgithub.com\u002Fdeusjin\u002Fsubforge.git\ncd subforge\ncargo install --path .\nsubforge --version\n```\n\nInstall Python-side dependencies:\n\n```bash\nsubforge setup                         # CPU PyTorch\nsubforge setup --compute cu124         # CUDA 12.x\nsubforge setup --compute cu128-nightly # RTX 50 \u002F Blackwell sm_120\nsubforge setup --force                 # rebuild the local venv\n```\n\nCreate local config:\n\n```bash\ncp config.toml.example config.toml      # Linux \u002F macOS\ncopy config.toml.example config.toml    # Windows cmd\n```\n\nThen verify the environment:\n\n```bash\nsubforge doctor\n```\n\n`config.toml` is ignored by Git because it may contain API keys.\n\n## Quick Start\n\nProcess a video end to end:\n\n```bash\nsubforge process video.mp4\n```\n\nGenerate translated subtitles without burning them into the video:\n\n```bash\nsubforge translate video.mp4\n```\n\nBatch translate a folder of videos into an output directory:\n\n```bash\nsubforge batch translate videos\u002F -o out\n```\n\nUse soft subtitle muxing instead of re-encoding the video:\n\n```bash\nsubforge process video.mp4 --synth-mode soft\n```\n\nGenerate both a hard-burned video and a soft-subtitle output:\n\n```bash\nsubforge process video.mp4 --synth-mode both\n```\n\nRun each stage manually:\n\n```bash\nsubforge transcribe video.mp4 --asr faster-whisper\nsubforge subtitle video.srt --translator google\nsubforge synthesize video.mp4 --subtitle video_translated.srt\n```\n\n## CLI Overview\n\n| Command | Purpose |\n| --- | --- |\n| `transcribe` | Audio\u002Fvideo to SRT |\n| `subtitle` | SRT to translated SRT |\n| `translate` | Transcribe and translate, without video synthesis |\n| `synthesize` | Video + SRT to hard-burned or muxed output |\n| `process` | Full pipeline: transcribe, translate, synthesize |\n| `batch` | Batch translate or process multiple videos |\n| `eval` | Subtitle quality evaluation with SubER and text metrics |\n| `setup` | Create Python venv and install sidecar dependencies |\n| `doctor` | Check ffmpeg, Python packages, CUDA, config, and sidecar sync |\n| `model` | List and download faster-whisper models |\n| `gpu` | Detect and select the default CUDA GPU |\n| `cache` | Show, prune, or clean cache entries |\n| `config` | Show, get, set, and locate configuration |\n\n## Batch Processing\n\nUse `batch translate` when you only want translated subtitle files. Use\n`batch process` when you want final videos with subtitles.\n\nPreview first:\n\n```bash\nsubforge batch translate videos\u002F -o out --dry-run\nsubforge batch process videos\u002F -o out --dry-run\n```\n\nRun the batch:\n\n```bash\nsubforge batch translate videos\u002F -o out\nsubforge batch process videos\u002F -o out --synth-mode soft\n```\n\nCommon patterns:\n\n```bash\n# Process multiple explicit files\nsubforge batch translate ep1.mp4 ep2.mp4 ep3.mp4 -o out\n\n# Scan subdirectories too\nsubforge batch translate videos\u002F -o out --recursive\n\n# Run two videos at a time\nsubforge batch process videos\u002F -o out --jobs 2\n\n# Rerun items whose final output already exists\nsubforge batch process videos\u002F -o out --overwrite\n\n# Write a machine-readable result report\nsubforge batch translate videos\u002F -o out --report batch-report.json\n```\n\nBatch behavior:\n\n| Rule | Behavior |\n| --- | --- |\n| Inputs | Accepts files and directories. Directory inputs scan one level by default. |\n| Recursive scan | Add `--recursive` to include nested directories. |\n| Output | Without `-o`, outputs are written next to each input video. With `-o DIR`, outputs are written under that directory. |\n| Existing outputs | Existing final outputs are skipped by default. Use `--overwrite` to rerun. |\n| Concurrency | `--jobs 1` by default. Raise it only when your GPU\u002FAPI limits can handle parallel work. |\n| Translation memory | If `tm_dir` is unset, batch creates one shared `.subforge-tm` for the batch to keep terminology consistent. |\n| Failure handling | Failed items are reported, remaining items continue, and the command exits non-zero if anything failed. |\n\n## Translation Backends\n\n| Backend | Mode | Notes |\n| --- | --- | --- |\n| `google` | Web translation | Free, no key required, simple concurrent per-cue path |\n| `bing` | Web translation | Free, requires `curl`, uses refreshable auth token handling |\n| `llm` | OpenAI-compatible API | Best quality path, supports terminology, memory, QE, and refine |\n| empty string | No translation | Useful for reformatting or synthesis only |\n\nExample:\n\n```bash\nsubforge subtitle input.srt --translator llm --target-language zh-Hans\n```\n\nFor LLM translation, configure:\n\n```toml\ntranslator      = \"llm\"\napi_key         = \"\"\nbase_url        = \"https:\u002F\u002Fapi.openai.com\u002Fv1\"\nmodel           = \"gpt-4o-mini\"\ntarget_language = \"zh-Hans\"\n```\n\nEnvironment variables take precedence:\n\n```bash\nexport OPENAI_API_KEY=\"...\"\nexport OPENAI_BASE_URL=\"https:\u002F\u002Fapi.openai.com\u002Fv1\"\n```\n\n## LLM Quality Pipeline\n\nThe LLM backend uses a staged pipeline:\n\n```text\nsource SRT\n  |\n  +-- terminology extraction\n  |     -> glossary.jsonl\n  |\n  +-- batched translation\n  |     -> chained mode or concurrent wave mode\n  |\n  +-- GEMBA-MQM quality estimation\n  |\n  +-- targeted refinement for low-score cues\n  |\n  +-- translation memory\n        -> memory.jsonl\n```\n\n`chained_translation = true` gives the best continuity because every batch sees\nthe previous translations before it runs. It is effectively sequential for the\nmain LLM translation stage.\n\n`chained_translation = false` warms up with three sequential batches and then\nruns later batches in concurrent waves. It is faster on long videos, while\npreserving cross-wave context.\n\n```toml\nchained_translation = true   # best continuity\nthread_num          = 3\nbatch_size          = 7\n```\n\nFor faster long-video runs:\n\n```toml\nchained_translation = false\nthread_num          = 5\nbatch_size          = 10\n```\n\n`batch_size` controls how many subtitle cues go into one LLM request.\n`thread_num` controls how many requests can run at the same time.\n\n## ASR Backends\n\n| Backend | Description | Configuration |\n| --- | --- | --- |\n| `faster-whisper` | Local Whisper transcription, recommended | `whisper_model`, `whisper_device` |\n| `bijian` | Bilibili Bcut endpoint | `bijian_base_url` |\n| `whisper-api` | OpenAI-compatible audio transcription | `whisper_api_model`, `asr_api_key`, `asr_base_url` |\n| `whisper-cpp` | Local whisper.cpp binary | `whisper_cpp_model` |\n\nFirst-time local model usage is interactive. SubForge lists available\nfaster-whisper models and downloads the selected one with Hugging Face progress.\nFor non-interactive environments, download in advance:\n\n```bash\nsubforge model list\nsubforge model download turbo\n```\n\n## Subtitle Synthesis\n\nHard burn subtitles:\n\n```bash\nsubforge synthesize video.mp4 --subtitle sub.srt --mode hard\n```\n\nSoft-mux subtitles without re-encoding:\n\n```bash\nsubforge synthesize video.mp4 --subtitle sub.srt --mode soft\n```\n\nCreate both outputs:\n\n```bash\nsubforge synthesize video.mp4 --subtitle sub.srt --mode both\n```\n\nStyle and encoder options:\n\n```bash\nsubforge synthesize video.mp4 --subtitle sub.srt \\\n  --font \"Source Han Sans\" \\\n  --font-size 24 \\\n  --font-color FFFFFF \\\n  --outline-color 000000 \\\n  --outline-width 3 \\\n  --position bottom-right \\\n  --encoder x265 \\\n  --crf 22 \\\n  --preset slow\n```\n\nSoft subtitle container behavior:\n\n| Output container | Subtitle codec |\n| --- | --- |\n| `.mp4`, `.m4v`, `.mov` | `mov_text` |\n| `.mkv` | SRT |\n| `.webm` | WebVTT, only when the source video is already WebM-compatible |\n\nIf in doubt, use `.mkv` for soft subtitles or use hard burn.\n\n## GPU Selection\n\nSubForge can pin faster-whisper and NVENC to a selected CUDA GPU:\n\n```bash\nsubforge gpu\nsubforge gpu --set 1\nsubforge config get cuda_gpu\nsubforge config set cuda_gpu \"\"\n```\n\nAt runtime, SubForge reports the physical GPU selected through\n`CUDA_VISIBLE_DEVICES`.\n\n## Configuration\n\nConfig lookup order:\n\n1. `--config \u003Cpath>`\n2. `$SUBFORGE_CONFIG`\n3. `.\u002Fconfig.toml`\n4. `$XDG_CONFIG_HOME\u002Fsubforge\u002Fconfig.toml`\n5. `$HOME\u002F.config\u002Fsubforge\u002Fconfig.toml`\n\nCommon options:\n\n| Key | Default | Description |\n| --- | --- | --- |\n| `asr` | `faster-whisper` | Speech recognition backend |\n| `whisper_model` | `small.en` | faster-whisper model |\n| `segmenter` | `sat` | Subtitle segmentation algorithm |\n| `translator` | `bing` | Translation backend |\n| `target_language` | `zh-Hans` | Target language |\n| `layout` | `target-above` | Subtitle layout |\n| `thread_num` | `3` | Concurrent request count |\n| `batch_size` | `7` | LLM subtitle cues per request |\n| `quality_estimation` | `true` | Enable GEMBA-MQM scoring |\n| `refine` | `true` | Retry low-score translations |\n| `synth_mode` | empty | `hard`, `soft`, or `both`; empty means hard |\n| `synth_encoder` | empty | `x264`, `x265`, `nvenc`, `nvenc-hevc`, `qsv`, `videotoolbox` |\n\nUse:\n\n```bash\nsubforge config show\nsubforge config get api_key\nsubforge config set whisper_model medium\nsubforge config path\n```\n\n`subforge config get api_key` only prints a redacted prefix.\n\n## Translation Memory\n\nBy default, project memory is stored beside the source video:\n\n```text\n.subforge-tm\u002F\n  glossary.jsonl\n  memory.jsonl\n  .lock\n```\n\nFor shared memory across multiple videos, set:\n\n```bash\nsubforge config set tm_dir \u002Fshared\u002Fpath\u002F.subforge-tm\n```\n\nThe memory writer uses an advisory lock so concurrent processes do not corrupt\nthe JSONL files.\n\n## Cache and Model Data\n\nRuntime data lives under `.subforge\u002F` by default:\n\n```text\n.subforge\u002F\n  cache\u002F\n  models\u002Ffaster-whisper\u002F\n  tools\u002Ffaster-whisper-cli\u002Fvenv\u002F\n```\n\nUseful commands:\n\n```bash\nsubforge cache stats\nsubforge cache prune --days 30 --max-mb 500\nsubforge cache clean\n```\n\n## Evaluation\n\nEvaluate subtitle quality against a reference SRT:\n\n```bash\nsubforge eval output.srt -r reference.srt\nsubforge eval output.srt -r reference.srt -l zh\n```\n\nThe eval path uses SubER plus text metrics such as WER, BLEU, chrF, and TER.\n\n## Development\n\n```bash\ncargo fmt --all -- --check\ncargo clippy --all-targets -- -A clippy::field_reassign_with_default\ncargo build --all-targets\ncargo test --all-targets\ncargo bench --bench client_reuse\n```\n\nThe Python sidecar at `scripts\u002Ftranscribe_segment.py` is embedded into the Rust\nbinary with `include_str!`. Rebuild the binary after editing it. `subforge doctor`\nchecks whether the embedded copy and the file on disk are in sync.\n\n## Security\n\n- `config.toml` is ignored by Git and may contain API keys.\n- `.subforge\u002F`, `.subforge-tm\u002F`, `target\u002F`, and test videos are ignored.\n- CI runs gitleaks against common API keys, bearer tokens, and JWTs.\n- Runtime retry errors redact secret-looking URL query parameters and bearer\n  tokens before printing them.\n\nDo not paste `config.toml`, `subforge config show`, or API gateway URLs with\ntokens into public issues.\n\n## Troubleshooting\n\n| Symptom | Fix |\n| --- | --- |\n| `cargo` not found | Install Rust with rustup, then restart the terminal |\n| `ffmpeg not found` | Install ffmpeg with your system package manager |\n| Python package missing | Run `subforge setup` |\n| CUDA unavailable | Rebuild the venv with `subforge setup --compute cu124 --force` |\n| RTX 50 \u002F Blackwell unsupported | Use `subforge setup --compute cu128-nightly --force` |\n| `subforge eval` cannot find SubER | Run `subforge setup` or install `subtitle-edit-rate` |\n| Bing translation fails because curl is missing | Install `curl` |\n| Edited Python sidecar has no effect | Rebuild with `cargo install --path .` |\n\nFor a full local check:\n\n```bash\nsubforge doctor\n```\n\n## Research References\n\nSubForge is engineering-oriented, but several stages are inspired by published\nsubtitle and translation quality work:\n\n- Segment Any Text, EMNLP 2024 - neural segmentation\n- Long-Form Speech Translation, Findings of EMNLP 2023 - context-aware subtitle work\n- GEMBA-MQM, WMT 2023 - LLM-based quality estimation\n- MAPS, WMT 2024 - terminology extraction and consistency\n- SubER, IWSLT 2022 - subtitle evaluation\n\n## Community\n\nThis project is promoted in the LINUX DO open-source community. Thanks to the\ncommunity for discussion, feedback, and suggestions.\n\n- [LINUX DO](https:\u002F\u002Flinux.do\u002F)\n\n## License\n\nMIT\n","SubForge 是一个基于 Rust 的命令行工具，用于处理视频字幕工作流，包括转录、分段、翻译、评估以及烧录或混合字幕。它利用了更快的语音识别技术（如faster-whisper），支持GPU加速，并且能够通过多种语言模型后端（如Google、Bing和OpenAI兼容的LLM）进行高质量的字幕翻译。此外，SubForge还提供了术语提取、项目级翻译记忆等功能以确保术语的一致性，并使用GEMBA-MQM方法估计翻译质量。该工具适用于需要重复处理视频内容的场景，例如本地化、课程翻译或创作者的工作流程，特别是当可重复性和自动化是关键需求时。",2,"2026-06-11 04:09:11","CREATED_QUERY"]