Table of Contents
- How-to guides
- Turn on unanchored repair for a dub-only show
- Queue a whole library, in watch order
- Redo an episode from scratch
- Recover an episode that crashed
- Hold a show until you have reviewed it
- Fix a repair stage that silently does nothing
- Choose a quantisation for your card
- Secure the review page
- Share your glossaries and verdicts
- Report a problem
How-to guides
Recipes. Each solves a single problem and assumes a working install — if you do not have one, start with Your first show.
Turn on unanchored repair for a dub-only show
Problem: your copies have an English dub but no English subtitle track for the Japanese audio. The repair stage has nothing to anchor against, refuses every line, and mangled names stay on screen.
Check whether this is you. List an episode's subtitle tracks:
ffprobe -v error -select_streams s \
-show_entries stream=index:stream_tags=language,title \
-of default=noprint_wrappers=1 "Show - S01E01.mkv"
An English track carrying full dialogue means you are anchored and should not do this. A Signs and Songs track only counts as no reference.
Seasons of one show can differ. Check the season you are about to process.
Do it. Add one key to that show's glossary file:
{
"show": "Show Name",
"unanchored_repair": true,
"names": ["..."]
}
The next repair pass sends glossary-only prompts for unanchored lines instead of skipping them.
Know the trade. Glossary-only repair can fabricate names — measured turning Oimo into
Zoro. Review the results rather than turning this on and walking away. Reasoning:
Why it works this way.
There is also a global REPAIR_UNANCHORED variable. Do not use it. It is recorded in no
committed file, which is precisely how a season's corrections were once silently reverted to
raw speech recognition.
Queue a whole library, in watch order
Problem: the default order spends hours on seasons you watched years ago before reaching the arc you are on.
Choose the shows and their order in /config/anime_order.txt, one folder name per line.
# comments and blanks are ignored:
One Pace (1999)
Cowboy Bebop (1998)
# Trigun (1998) <- queued but paused
Jump to where you actually are with /config/season_priority.txt:
# Show folder name : start season
One Pace (1999):20
Seasons at or after the start go first, ascending; earlier ones wrap around after. The same episodes, resequenced so the next arc lands soonest.
This file has no default path. Point at it explicitly:
SEASON_PRIORITY_FILE: /config/season_priority.txt
Without that variable the log says watch-order disabled and the file is never read.
SEASON_START is a global fallback for when you have no per-show file.
When you move further along, bump the number and restart the container.
Redo an episode from scratch
Delete its sidecars and completion stamp:
rm "Show - S01E05".dubtitles.done \
"Show - S01E05".eng.dubtitles.srt \
"Show - S01E05".eng.dubtitles.ass \
"Show - S01E05".dubtitles.conf.json
The next sweep re-transcribes it.
To redo only the text, not the audio pass, keep .dubtitles.words.json. Word-level
confidences live there, so the text tier replays without the GPU.
This does not remove the already-muxed track. To strip it from the video first:
mkvmerge -o out.mkv --subtitle-tracks '!<id>' in.mkv
Recover an episode that crashed
Problem: one episode is skipped every sweep.
A hard crash leaves a poison marker so the pipeline does not loop on it:
ls "Show - S01E05".dubtitles.fail
cat "Show - S01E05".dubtitles.crash.json
The crash file names the exception. When you have dealt with it — or want to try anyway — delete the marker:
rm "Show - S01E05".dubtitles.fail
Hold a show until you have reviewed it
Problem: you do not want unreviewed output written into videos for a show you care about.
REVIEW_GATE_SHOWS: "One Pace (1999):Cowboy Bebop (1998)"
Colon-separated directory names. Those episodes are transcribed and repaired as usual but are not muxed until their review queue is settled.
A hold older than REVIEW_GATE_STALE_DAYS (default 7) is reported loudly and stays held.
The timer buys a warning, never a release — auto-releasing unreviewed output is the thing
the gate exists to prevent.
Empty by default: an install that has not opted in behaves exactly as before.
Fix a repair stage that silently does nothing
Problem: the pipeline runs, the log shows repair targets, and every line comes back unchanged. No errors.
Check the model is actually replying:
curl -s http://127.0.0.1:8090/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"messages":[{"role":"user","content":"Reply with the word ok"}],"max_tokens":8}' \
| python3 -m json.tool
Look at choices[0].message. Two failures look identical from the pipeline's side:
| What you see | Cause | Fix |
|---|---|---|
content empty, reasoning_content full |
The model is thinking and never answers | --jinja and --chat-template-kwargs '{"enable_thinking":false}' |
content empty, nothing else |
The chat template was never applied | --jinja |
--jinja is required for chat_template_kwargs (specifically enable_thinking:false,
which repair.py always sends) to take effect at all. Without it the server loads, answers
/health with 200, and a thinking-capable model — qwen3-4b-instruct, the default,
included — spends its whole budget on reasoning and returns empty content forever.
Do not use /health as a readiness check. It reports 200 while weights are still
loading. Ask for a real completion instead, as above.
Choose a quantisation for your card
Problem: you want Whisper and the repair model resident at once on one card.
This project runs Q6_K. On-disk weights, measured 2026-09-02:
| Quantisation | Size on disk |
|---|---|
Q8_0 |
4.0 GB |
Q6_K |
3.1 GB |
Q5_K_M |
2.7 GB |
Q4_K_M |
2.4 GB |
On a 6 GB card, Q8_0 leaves roughly 2 GB — tight alongside Whisper, and not necessarily enough depending on your Whisper model and compute type. Q6_K buys back most of a gigabyte, which is why it is the one this project settled on.
Options, cheapest first:
- A smaller quantisation. Q5_K_M or Q4_K_M go further still. The quality cost below Q6_K is not measured here; the table above is sizes, not quality.
- Two cards. Whisper on one, the repair model on the other. Nothing assumes they share a device — the repair stage talks to an HTTP endpoint.
- Sequential. Run the transcription sweep and the merge sweep at different times, one
model resident at a time.
generate.pyloads Whisper lazily and its process exits between shows, so this costs wall-clock rather than quality.
Check what is actually resident before blaming the pipeline for an OOM:
nvidia-smi --query-gpu=memory.used,memory.total --format=csv
If another process holds the card, Whisper can CUDA-OOM. The pipeline exits cleanly and the loop restarts with a fresh context, so it self-heals once VRAM frees — but do not run two transcriptions on one small card at once.
Secure the review page
Problem: the review server's write routes rewrite subtitles and force re-muxes, from a root-owned process.
The default is already safe. With REVIEW_TOKEN unset a token is generated, persisted
0600, and printed once:
docker exec <container> cat /config/review_token
Write routes require it in an X-Review-Token header. Read routes never require it — a
GET can enumerate your library.
To set your own:
REVIEW_TOKEN: "a-long-random-string"
To disable authentication entirely, set it to the empty string. Only an explicit empty value does this, and it is a decision about your own network:
REVIEW_TOKEN: ""
Do not expose port 8842 to the internet either way.
Share your glossaries and verdicts
Problem: curated glossaries and reviewed seasons are trapped on one machine.
GLOSSARY_DIR and DECISIONS_DIR are plain directories of JSON, one file per show, designed
to be committed to git. A git pull on the host is what makes them current — the container
reads whatever is in the mount.
cd ~/dubtitlerr/glossaries && git init && git add . && git commit -m "glossaries"
The review token is deliberately stored beside DECISIONS_DIR, never inside it, so a
credential never rides along with a directory you publish.
Community glossaries live in their own repository with a pull-request path for contributions.
Report a problem
Include:
- The log lines around the failure. Unvalidated-configuration warnings are formatted to be pasted directly.
.dubtitles.repair-summary.json— it names the model, the rules and the counts..dubtitles.crash.json, if there is one.- Whether the show is anchored or unanchored, plus the
ffprobetrack listing from the first recipe on this page.
Do not include the video.