subtitle-workbench
v0.2.0
Published
Convert Blu-ray PGS and DVD VobSub subtitles to SRT, locally.
Maintainers
Readme
Subtitle Workbench
Convert image-based subtitles to SRT, locally. Nothing is uploaded anywhere — the OCR, the decoding and the file writing all happen on your machine.
| Tool | Input | What it does |
| --- | --- | --- |
| SUP to SRT | .sup | OCR for Blu-ray PGS subtitle tracks |
| SUB/IDX to SRT | .sub + .idx | OCR for DVD VobSub subtitle pairs |
| Extract from Video | .mkv | Pull embedded subtitle tracks out of a video |
Use it as a desktop-style app in your browser, or as a CLI for batch and automation work.
Install
Subtitle Workbench runs on your own computer and is built on a few well-known free tools. Installing means three steps: get Node.js, get the media tools, then get Subtitle Workbench itself. Every command below is typed into a terminal — Terminal on macOS (find it with Spotlight), PowerShell on Windows (right-click Start → Terminal), or your usual shell on Linux. Copy a line, paste it, press Enter, let it finish.
Step 1 — Node.js (the runtime this app is written for)
Install Node.js 22.13 or newer. Node is maintained by its own project and updates through the same channel you install it from — Subtitle Workbench deliberately does not bundle its own copy, so security updates to Node reach you the normal way.
- macOS:
brew install node(or the installer from nodejs.org — choose "LTS") - Windows:
winget install OpenJS.NodeJS.LTS(or the nodejs.org installer) - Debian/Ubuntu:
sudo apt install nodejs npm
Check it worked — this should print a version number, v22 or higher:
node --versionStep 2 — the media tools
These do the heavy lifting (video reading, image work, text recognition).
Install Tesseract 5.5 or newer. 5.4 recognises some low-contrast,
drop-shadowed subtitle frames as empty, and an empty result drops the cue
entirely — so the failure is a missing subtitle rather than a visibly wrong
one. doctor warns if it finds an older build.
macOS:
brew install ffmpeg tesseract imagemagick mkvtoolnixDebian / Ubuntu (
zenitypowers the file-picker dialog; most desktops already have it):sudo apt install ffmpeg tesseract-ocr imagemagick mkvtoolnix zenityWindows:
winget install Gyan.FFmpeg tesseract-ocr.tesseract ImageMagick.ImageMagick MoritzBunkus.MKVToolNixWindows only: close the terminal completely and open a new one afterwards — newly installed tools are not visible to a terminal that was already open.
Windows only: Tesseract and MKVToolNix's installers do not add themselves to your
PATH, even after a restart (FFmpeg and ImageMagick's installers do). Ifdoctorstill reportstesseractormkvmergeas missing after a fresh terminal, add these two folders to yourPATHyourself: open Settings → System → About → Advanced system settings → Environment Variables, edit your userPathvariable, and addC:\Program Files\Tesseract-OCRandC:\Program Files\MKVToolNix. Then open a new terminal again.Windows only: the widely-recommended
UB-Mannheim.TesseractOCRpackage is not what to install here — it stops at 5.4.0, which is below the version floor above.tesseract-ocr.tesseractis the upstream project's own package and tracks 5.5.
Step 3 — Subtitle Workbench
Once this package is published (until then, use "From source" below):
npm install -g subtitle-workbenchStep 4 — check everything
subtitle-workbench doctordoctor inspects every tool, prints the version it found, and — if
anything is missing — the exact install command for your platform. When it
ends with "All required dependencies are available", you are done:
subtitle-workbench uiopens the app in your browser. The app also runs this same check on startup and shows a warning with instructions if something is missing — when everything is in place you see nothing.
From source (developers, or before the npm release)
git clone https://github.com/namjins/subtitle_workbench.git
cd subtitle_workbench
npm install
npm run doctor
npm run appThe examples below use npm run cli --, which works from a source checkout.
With the package installed globally, replace npm run cli -- with
subtitle-workbench — the commands are otherwise identical.
Run the app
npm run appThat builds the interface and serves it at http://127.0.0.1:8765. Each tool
follows the same three steps: Intake (add files), Review (check them and
choose a language), Run.
The page is served by a small local server that does the actual work. It only accepts requests from the page it served itself, so no other site you have open can reach it.
Use the CLI
npm run cli -- --helpBlu-ray PGS subtitles
npm run cli -- sup-to-srt movie.sup --lang eng
npm run cli -- sup-to-srt *.sup --lang eng --out-dir ./srtOn Windows, PowerShell does not expand *.sup for you — pass the files
explicitly:
npm run cli -- sup-to-srt (Get-ChildItem *.sup).FullName --lang eng --out-dir ./srtDVD VobSub subtitles — pass the .idx; the matching .sub must sit beside it.
npm run cli -- subidx-to-srt movie.idx --lang engExtract subtitles from video files
npm run cli -- extract-english /path/to/videos
npm run cli -- extract-english /path/to/videos --languages eng,spa
npm run cli -- extract-english /path/to/videos --all-languagesScans the top level of a folder for .mkv files, writing .sub+.idx for DVD
VobSub tracks and .sup for Blu-ray PGS. Existing outputs are skipped.
Preview before a long run
npm run cli -- peek-sup movie.sup --out-dir ./preview --count 3Writes a few subtitle images so you can confirm the language before OCR'ing a whole disc.
Common options
| Option | Meaning |
| --- | --- |
| --out FILE / --out-dir DIR | Where to write. Defaults to beside the input, named movie-eng.srt (the language is part of the name so tracks in different languages do not overwrite each other). |
| --jobs auto | N | Parallelism. auto reserves one core for the rest of your machine. |
| --lang eng | OCR language. Needs matching Tesseract language data installed. |
| --ocr-engine auto | See below. |
| --text-cleanup generic | fitted | OCR text cleanup profile. fitted applies corrections tuned on the reference corpus. |
| --skip-existing | Leave already-converted files alone. |
| --no-cache | Reconvert even when a cached result exists (see below). |
| --quiet | Suppress per-cue progress. |
Finished OCR conversions are cached by the content of the source file, so
converting the same disc again — under any filename, to any destination — is
instant. The cache invalidates itself when the recogniser changes, when this
app's output format is revised, or when a better engine becomes available
(installing the Xcode Command Line Tools on a Mac); pass --no-cache to force
a reconversion at any time. Only the latest result is kept per source.
Where things are stored
| What | Where |
| --- | --- |
| macOS | ~/Library/Caches/subtitle-workbench |
| Windows | %LOCALAPPDATA%\subtitle-workbench\Cache |
| Linux | $XDG_CACHE_HOME/subtitle-workbench (default ~/.cache/...) |
Conversion results, OCR scratch space, and the compiled Vision helper all live under that one directory. Nothing evicts it automatically, so it grows with the number of distinct sources converted — deleting the whole directory is always safe and only costs reconversion time.
Environment variables
| Variable | Effect |
| --- | --- |
| SUBTITLE_WORKBENCH_CACHE_DIR | Override the cache directory above. |
| SUBTITLE_WORKBENCH_BRIDGE_PORT | Port for ui / the bridge (default 8765). |
| SUBTITLE_WORKBENCH_BRIDGE_HOST | Bind address for the bridge. Non-loopback values are refused by the bridge's own Host check — this cannot be used to share the UI on a network. |
| SUBTITLE_WORKBENCH_OCR_COMMAND | External recogniser command (see "Bringing your own recogniser"). |
OCR engines
auto chooses per format and, for Blu-ray tracks, per disc — because no
single engine wins everywhere:
| Format | Engine | | --- | --- | | SUP (PGS), macOS with Xcode Command Line Tools | probes each track, picks Tesseract or Apple Vision | | SUP (PGS), elsewhere | Tesseract | | SUB/IDX (VobSub), macOS with Xcode Command Line Tools | Apple Vision | | SUB/IDX (VobSub), elsewhere | Tesseract |
Apple Vision needs the Swift compiler from the Xcode Command Line Tools
(xcode-select --install). Without it, macOS quietly uses Tesseract like
every other platform — doctor reports whether swiftc was found.
Preprocessing adapts to each disc's rendering style per image — drop shadows, low-contrast fills, and hollow outline-drawn glyphs are detected and repaired before recognition — so Tesseract results are close to Apple Vision's on every tested disc style. On macOS the Blu-ray converter additionally reads a couple dozen frames with both engines first and keeps whichever handles that disc better, which makes Vision the safety net for styles the repairs cannot fix; Windows and Linux have no such net, and rare hollow-outline DVD fonts still convert worse there.
Override with --ocr-engine tesseract-accurate, tesseract-hybrid (faster,
less accurate) or macos-vision.
Bringing your own recogniser
--ocr-engine external-command hands each subtitle image to a program you
supply:
npm run cli -- subidx-to-srt movie.idx \
--ocr-engine external-command \
--ocr-command /path/to/your-ocrIt is invoked as your-ocr /path/to/image.png eng and may print plain text, or
JSON for richer results:
{ "text": "Hello", "confidence": 0.98, "model": "your-model" }Languages
English is what this has been validated against. Other languages work when the
matching Tesseract language data is installed — npm run cli -- doctor --lang deu
will tell you whether it is. Apple Vision supports its own set and warns when a
requested language is not among them.
Checking output quality
With reference SRT files, benchmark-ocr compares by timestamp, cue count,
exact text and character error rate:
npm run cli -- benchmark-ocr --reference reference.srt --candidate generated.srt
npm run cli -- benchmark-ocr --examples-dir ./references --candidate-dir ./outputAdd --timing-first when the reference text is imperfect but its timings are
trustworthy — it foregrounds missing, extra and shifted cues over text accuracy.
Development
npm test # unit and end-to-end; no build or network required
npm run lint
npm run typecheck
npm run build
npm run dev # UI dev server; run `npm run cli -- ui --dev` alongside itTests run against small fixtures in tests/fixtures/.
Desktop app (work in progress)
src-tauri/ holds a Tauri shell around the same UI: it starts the local
bridge on a private port and opens a native window on it, so the bridge's
job queue, authorization and native file picking are shared with the web
version.
Note the version coupling: the window loads the UI served by the installed
subtitle-workbench npm package, not a copy bundled into the app — so
updating one without the other runs the npm package's UI, whatever its
version. The page shows the version it is actually running next to its title.
Installing the desktop app without the npm package does not work at all; the
shell's error dialog walks through installing it.
It needs a Rust toolchain:
npm run app:desktop # tauri dev: builds the UI, compiles the shell, opens the windowWindows only: Rust alone is not enough. The default toolchain is
x86_64-pc-windows-msvc, which links with Microsoft's linker, so you also
need the C++ build tools:
winget install Microsoft.VisualStudio.2022.BuildTools --override "--wait --passive --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended"Installing rustup via winget skips the interactive check that would
otherwise warn you about this. Without the build tools the compile fails
partway through with a misleading error — in Git Bash, coreutils' link
gets picked up in place of the absent link.exe and reports
link: extra operand, which has nothing to do with the real cause.
Licence
PolyForm Noncommercial 1.0.0 — free to use, modify, and share for any noncommercial purpose: personal use, hobby projects, research, education, charities, public institutions. What it does not allow is commercial use — selling this software, charging for it, or building a paid product or service on it. If you want a commercial licence, open an issue.
