Correct chapter marks for your audiobooks — by actually listening to them.
ABChapterize is a command-line tool that scans audiobook files
(.m4a, .m4b, .mp3, .opus, .mka) for
spoken chapter announcements ("Chapter Seven", "Kapitel 12", …) using
Whisper speech recognition and
writes proper chapter marks directly into the file.
No splitting, no sidecar files, no server — the audio itself stays untouched,
only the chapter metadata is rewritten.
If you have ever bought an audiobook whose chapter marks were missing, misplaced, or pure fantasy (looking at you, Audible), this tool is for you.
Finding the chapters is only half the job. ABChapterize's mission is getting each mark to land in the right place — something most other tools treat as an afterthought, dropping the mark wherever the nearest silence happened to be, or a fixed handful of seconds off. Here, a found chapter mark is re-listened to in order to pin down where the announcement actually begins, jingles are located with a voice-activity model rather than guessed at from the waveform, and the mark is placed relative to what the narrator is doing rather than to the file's byte positions. Skipping ahead should land you on the chapter announcement (or - if you prefer - on the start of the chapter's lead-in jingle), not two sentences into the previous chapter's ending.
Prebuilt binaries for Windows and Linux are available on the Releases page — no build tools required.
- Finds chapters by listening — it detects the narrator's actual chapter announcements, not just gaps in the audio, by first probing short candidate windows at silences and jingles and only falling back to transcribing a full suspicious region when that isn't enough — keeping the time-consuming Whisper work to a minimum.
- Writes marks in place, safely — chapters are written by stream-copy
remuxing into a temporary file that is verified before it atomically replaces
the original. Your audiobook cannot be lost, even without
--backup. - Jingle-aware probing, by default — Pass 2's probe window is sized to
catch a jingle (a music sting) before the announcement, and a bundled
voice-activity model (Silero VAD) finds jingles even when they abut speech
with no silence on either side, which a plain amplitude scan would miss
entirely.
--mark-before-jingleanchors the written mark to the jingle/silence itself instead of the default fixed offset before the phrase. - Self-healing — when the detected chapter numbers have gaps (e.g. chapter 12 was announced without a pause before it), the suspicious regions are transcribed in full to find the missing ones.
- Zero setup for models — the Whisper model is downloaded automatically on first use, and checked against pinned SHA-256 and SHA3-256 digests before it is used — a compromised or tampered-with download is rejected rather than silently loaded.
- GPU accelerated — uses CUDA or Vulkan when available, falls back to CPU.
On a laptop or desktop with both an integrated and a discrete GPU, the
discrete one is picked automatically (left alone, the Vulkan runtime may
well grab the integrated one — measured 8.6x slower on a test machine), and
the GPU in use is named in the startup line. Override with
--use-gpu <name>;--list-gpusshows the names. - Processes batches in parallel — multiple files at once, auto-throttled to
live CPU load (and capped at 1 concurrent file on GPU backends by default,
for VRAM/context safety); override with
--jobs. - Batches survive being interrupted — name as many files and folders as you like in one command; each folder quietly keeps track of what is already done while it is being worked on, so a run stopped by Ctrl+C, a crash or a power loss picks up where it left off when you run it again. A run that finishes normally cleans up after itself.
- Detects the language automatically — by default, each file's language
is detected from a short clip before transcription and used just for that
file (falling back to English when the detection is inconclusive), so a
mixed-language collection just works with no
--langat all. Pin it to one language with--lang deif you'd rather skip per-file detection. - Eleven languages of number recognition out of the box — English, German,
French, Spanish, Italian, Dutch, Turkish, Portuguese, Polish, Swedish and
Danish. Whisper likes to write numbers out as words ("twenty-one",
"einundzwanzig", "vingt et un", "veintiuno", "ventuno", "eenentwintig",
"yirmi bir", "vinte e um", "dwadzieścia jeden", "tjugoett", "enogtyve"),
and ABChapterize understands them all; other languages work with a custom
phrase/regexp. Ordinal
announcements are understood too, before or after the phrase — "Erstes
Kapitel", "2. Kapitel", "chapitre premier", "Birinci Bölüm" — and
--langlocalizes the chapter phrase and title defaults (per-file when auto-detecting), so a German audiobook alone finds and writes "Kapitel" with no options at all. Missing your language? Adding one is a contained, well-signposted job — see doc/adding-a-language.md. - All chapter-capable audio formats — MP4 audiobooks (
.m4a/.m4b), MP3, Opus and Matroska audio (.mka). (.oggand.flacare out, through no fault of their own: ffmpeg cannot write chapter marks into those containers.).m4bis recommended:.m4aand.m4bare identical containers, but players may pick their exact behavior based on which extension they see, and chapters in.mp3/.opusfiles are honored by comparatively few players. - Windows and Linux, single self-contained executable.
ABChapterize uses ffmpeg/ffprobe for audio decoding and chapter writing.
If you don't have it yet:
- Windows: download a build from ffmpeg.org
(e.g. the gyan.dev "essentials" zip) and unpack it. ABChapterize finds it
automatically in
PATH, in anffmpegfolder next to the exe or in your user profile, in Program Files, or whereverFFMPEG_DIRpoints — at the unpacked folder or at the binaries themselves, either works. - Linux:
sudo apt install ffmpeg(or your distribution's equivalent).
Download the archive for your platform from the
Releases page and unpack it anywhere you like.
Keep the runtimes folder next to the executable — it contains the native
Whisper libraries.
abchapterize "My Audiobook.m4b"
That's it. On the first run, the speech model is downloaded automatically (about 1.6 GB for the default model — one time only, with a progress display). Then the audiobook is scanned and the chapter marks are written:
Whisper model "turbo" loaded (Vulkan backend on NVIDIA GeForce GTX 1070, auto language detection), 1 file(s) to process.
My Audiobook.m4b: 23 chapter(s) written (1-23) + intro, language: en (p=1.00)
Want to be extra careful on the first try? Use --backup — the original file
is kept as My Audiobook.m4b.bak, and abchapterize --revert restores it if you
don't like the result.
# A whole audiobook collection, subfolders included, keeping backups:
abchapterize --recurse --backup "D:\Audiobooks"
# Several folders and single files in one go, in the order given. If this run
# is interrupted, repeating the command resumes it instead of starting over:
abchapterize --recurse "D:\Audiobooks" "E:\New arrivals" "F:\one-off.m4b"
# A mixed-language collection: no --lang needed, each file's language is
# detected automatically and localizes "Kapitel"/"Chapter"/etc. per file:
abchapterize --recurse "D:\Audiobooks (mixed languages)"
# Pin a fixed language instead of auto-detecting per file (skips detection,
# slightly faster): the chapter phrase and title default to "Kapitel"
# automatically with --lang de:
abchapterize --lang de buch.m4b
# The publisher plays a jingle before each chapter announcement — jingle-aware
# probing (and the VAD pre-pass) run by default, no flag needed:
abchapterize hoerbuch.m4b
# ...want the mark anchored to the jingle/silence itself instead of the
# default fixed offset before the phrase?
abchapterize --mark-before-jingle hoerbuch.m4b
# Redo files that already have (wrong) chapter marks:
abchapterize --force badly-marked.m4b
# Not sure which files in a big collection have good marks and which don't?
# Check each existing mark against the audio; only the bad ones get redone:
abchapterize --recurse --verify "D:\Audiobooks"
# Not sure a --filter regexp actually matches the files you mean? List them
# without touching Whisper, ffmpeg or any file:
abchapterize --no-op --filter "/brandon sanderson/" --recurse "D:\Audiobooks"
# See what would be detected without writing anything:
abchapterize --dry-run "My Audiobook.m4b"
# Retry only the files a previous run couldn't fully chapterize (tagged
# "<name>.missing-marks-<n>-<n>-....<ext>" - see the manual's troubleshooting
# section): running over them again resumes automatically, re-probing only
# the still-tagged gap(s) - no extra flag needed, and the rest of a big
# collection is left untouched either way:
abchapterize --recurse --filter "/\.missing-marks-/" "D:\Audiobooks"
# ...or discard that partial work and start such a file over from scratch:
abchapterize --recurse --force --filter "/\.missing-marks-/" "D:\Audiobooks"
# Batch run: quiet, but with a summary at the end:
abchapterize -rqs "D:\Audiobooks"
# Detect, write, and also save a sidecar for manual review/correction:
abchapterize --export "My Audiobook.m4b"
# ...fix a chapter title in My Audiobook.m4b.chapters.ffmeta, then apply it
# without re-running Whisper:
abchapterize --import --force "My Audiobook.m4b"
# Processing several files at once, auto-throttled to CPU load, is already the
# default for any multi-file run - no flag needed. Force a fixed number of
# concurrent files instead of the automatic ceiling:
abchapterize --recurse --jobs 4 "D:\Audiobooks"Run abchapterize --help for a quick reference, or see the
manual for the full story — including exactly
what is kept and what is stripped
when chapters are written. Grouped below exactly as --help groups them:
File selection
| Option | What it does |
|---|---|
-r, --recurse |
Descend into subdirectories. |
-F, --filter <f> |
Only process matching files: /regexp/ (against the whole path) or an extension list like mp3,m4b. |
-f, --force |
Redo files that already have chapter marks. |
-x, --max-chapters <n> |
Treat more than <n> pre-existing marks as bogus and discard them. |
Detection tuning
| Option | What it does |
|---|---|
-l, --lang <code|auto> |
Language hint for Whisper, or auto (the default): each file's language is detected from a short clip and used for that file, falling back to en when the detection is inconclusive. Numbers transcribed as words — cardinal and ordinal, before or after the phrase — are understood in en, de, fr, es, it, nl, tr, pt, pl, sv, da; digits (12, 2nd, 2e) and Roman numerals (XIII) in every language. Also localizes the defaults of --chapter-phrase, --prologue-phrase, --epilogue-phrase, --title, --intro-title, --prologue-title and --epilogue-title (per-file with auto). |
-c, --chapter-phrase <p> |
Word or /regexp/ announcing a chapter (default: /chapter/, localized by --lang). |
-p, --prologue-phrase <p> |
Word or /regexp/ announcing a prologue (default: /prolog/, localized by --lang). Only accepted before the first numbered chapter, at most once per file; an empty value switches prologue detection off. |
-g, --epilogue-phrase <p> |
Word or /regexp/ announcing an epilogue (default: /epilog/, localized by --lang). Only accepted after at least one numbered chapter, at most once per file; an empty value switches epilogue detection off. |
-u, --custom <mappings> |
Extra phrase:title mappings separated by ;, e.g. --custom "zwischenspiel:Zwischenspiel;/zeit[- ]?tafel/:Zeittafel". A phrase is a word or a /regexp/, parses no number, and is matched at any point in the file as often as it occurs (up to 100 marks per file), but must be announced just as a chapter phrase must. Titles may reference the phrase's capturing groups as $1, $2 or by name. Repeatable; never localized. |
-U, --custom-file <path> |
Read --custom mappings from a text file, one per line (blank and # lines ignored). |
--ignore-chapter-numbers |
Detect chapter announcements as usual but form no opinion about their numbers: the spoken number still reaches the title, nothing checks the sequence. Passes 2.5 and 3 never run and nothing is ever tagged .missing-marks. Cannot combine with --pass3-model, --expected-start-chapter, --max-chapter-number, --trailing-scan or --verify. |
-m, --model <name> |
Whisper model: tiny, base, small, medium, turbo (default), large, or custom:<path> for a GGML model file of your own (used as-is: not downloaded, not checksum-verified, ranked against the built-in models by file size). tiny/base are not recommended for real audiobooks (see Tuning tips). |
-M, --pass3-model <name> |
Whisper model for pass 3 (gap filling) only; same choices as --model, custom:<path> included (default: same as --model). Lighter to speed pass 3 up, or large for one last attempt at the gaps. Loaded lazily, only if pass 3 runs. |
-C, --cpu-only |
Force Whisper onto the CPU backend instead of the fastest available hardware acceleration. The Silero VAD pre-pass already always runs on CPU regardless of this option, so it only affects Whisper. |
--use-gpu <name> |
Run Whisper on the GPU whose name contains <name>, case-insensitively — --use-gpu gtx, --use-gpu uhd. Only needed to override the automatic preference for a single discrete GPU, or to choose between several discrete ones. A request matching no GPU, or more than one, is an error listing the real names. Vulkan only. See picking a GPU. |
-j, --mark-before-jingle |
Walk the mark backward from the default placement, back through the jingle's own music, to the end of the previous chapter's actual narration — or to the start of the last jingle, where several play back to back — instead of the default fixed offset before the phrase (see How it works). Where the walk ends on a pause, the mark backs into it by -k seconds; a chapter with no jingle at all keeps its ordinary placement. Best left alongside the default refinement: with -Q the walk starts from raw default placement, which occasionally overshoots the announcement and leaves the mark after it. |
-Q, --quick-marks |
Experimental. Skip the refinement that normally re-transcribes the audio at every mark to confirm the phrase is really there (see How it works). Faster — saves a handful of transcriptions per chapter — but marks, while usually usable, may end up after the chapter phrase rather than before it, even together with -j. |
-X, --max-jingle-length <s|auto> |
Longest expected jingle in seconds; this is always the probe window's ceiling (default, and ceiling with auto: 45), or 0 for "no jingle expected at all" — narrows the probe window back down and skips the VAD pre-pass (unless -j still needs it). With auto (the default), the probe window self-tightens after every jingle mark found (see How it works); an explicit value keeps the window fixed at it instead. |
-k, --mark-lead <seconds> |
How far in front of the announcement a mark is placed (default 0.35). Purely a matter of taste — marks are located just as precisely whatever this is, it only decides how much lead-in you hear before the narrator starts. Below roughly 0.2 the announcement's opening consonant starts to get clipped; 0 marks the onset itself. Applies with -j too: in full where a chapter has no jingle, and as a back-off into the pause before the jingle where there is one (capped at that pause's length). |
-n, --min-silence-length <s|auto> |
Silence duration that counts as a potential chapter break; this is always the silence scan's floor (default, and floor with auto: 1.5). With auto (the default), the probing threshold self-tightens after every mark found (see How it works); an explicit value probes every such silence instead. |
Detection safety nets
| Option | What it does |
|---|---|
-a, --early-abort <minutes> |
Always on (default: 60; 0 disables it). Give up on a file, unchanged, once this many minutes of play time have been probed with no chapter found — avoids transcribing a whole book that plainly isn't going to yield any. Only applies to a fresh detection run. |
-e, --expected-start-chapter <n> |
For a split-book part that doesn't start at chapter 1: the number this file is expected to start at. Without it (the default), whatever number pass 2 finds first is trusted outright and nothing below it is ever searched for. With it, a first chapter found below <n> aborts the file outright, unchanged; a first chapter found above <n> has pass 3 search for the missing numbers down to <n>, tagging the file .missing-marks-… if it still can't find them all. Only applies to a fresh detection run. |
-L, --trailing-scan |
Transcribe everything after the last chapter found, through to the end of the file, looking for further chapters (default: off). A missing chapter is normally spotted as a hole in the number sequence, which needs a known chapter on either side of it — so one missing after the last chapter found is the one case nothing notices, and the file comes out looking complete. This closes that hole, but there are no expected numbers to satisfy here, so the scan can never stop early: every file pays a full final chapter's worth of transcription, whether or not anything was wrong. |
-N, --max-chapter-number <n> |
Highest chapter number this book plausibly has (default: no limit). A detected chapter numbered above <n> is discarded as a mishearing — without it, one misheard "chapter 510" in a twelve-chapter book turns everything in between into a gap to hunt for. Not to be confused with --max-chapters above, which counts pre-existing marks. |
-V, --verify |
Check pre-existing chapter marks against the audio instead of trusting them blindly (or requiring --force): marks that check out are trusted and kept, and only the stretch(es) of the file around any mark that doesn't get redetected. If every mark fails, the file falls back to full detection. Cannot combine with --force or --import. |
-h, --verify-threshold <n> |
Requires --verify. If more than <n> marks fail verification, the ones that passed are no longer trusted as gap-recovery anchors either — the whole file falls back to full detection, same as when nothing at all is confirmed. |
Chapter titles
| Option | What it does |
|---|---|
-t, --title <word> |
Word for generated chapter titles (default: Chapter, localized by --lang). |
-i, --intro-title <word> |
Title for the intro mark before the first detected mark (default: Intro, localized by --lang). |
-P, --prologue-title <word> |
Title for the prologue mark (default: Prologue, localized by --lang). |
-G, --epilogue-title <word> |
Title for the epilogue mark (default: Epilogue, localized by --lang). |
Output & review
| Option | What it does |
|---|---|
-d, --dry-run |
Detect chapters but write nothing; print what would be written. |
-E, --export |
Also save detected chapters to a sidecar file (<file>.chapters.ffmeta, or <file>.chapters.txt with --simple-metadata) for manual review or correction. Combinable with --dry-run. |
-I, --import |
Skip Whisper entirely and write chapters from a previously exported sidecar file instead — for reapplying a hand-corrected result. |
-S, --simple-metadata |
Use a plain H:MM:SS.fff Title sidecar format instead of FFMETADATA for --export/--import. |
File & backup management
| Option | What it does |
|---|---|
-b, --backup |
Keep the original file as *.bak. |
-R, --revert |
Restore all *.bak backups (undo). |
-O, --no-op |
List every file --filter (and --recurse) would select, then exit without loading Whisper, invoking ffmpeg or touching any file — a quick way to check a --filter regexp or extension list before a real run. Requires --filter; combinable only with --recurse and the output options. |
--ignore-progress |
Start every listed folder over instead of resuming it. While a folder is being processed, the files finished so far are recorded in an .abchapterize-progress file inside it, which is deleted again as soon as that folder is done — so an interrupted run resumes by itself when the same command is run again. Progress recorded under different options is discarded automatically, so this is only needed to redo files the very same command already finished. |
Logging & display
| Option | What it does |
|---|---|
-q, --quiet |
Suppress the per-file lines; warnings and errors are still shown. |
-v, --verbose |
Log processing details, each probe/chunk as a <length>@<time> header. |
-T, --verbose-transcripts |
Like --verbose, but also dump every Whisper transcript's segments. Implies --verbose. |
-o, --log-file <path> |
Write the log to a file instead of the console — switches logging on by itself (add -T for the transcripts). The console keeps its progress bar and result lines, which the file gets too. Appends to an existing file. |
-B, --no-bar |
No progress bar; per-file results as log lines. |
--color <mode> |
Colorize the progress bar and the --summary block: auto (default), always or never. Nothing else is ever colored, and a --log-file always gets plain text. |
-s, --summary |
Totals at the end of the run: file counts, times, and confidence, silence/jingle, Whisper-audio and transcription-speed statistics. |
Performance
| Option | What it does |
|---|---|
-J, --jobs <n|auto> |
Number of files processed concurrently (default: auto — adjusted between 1 and a hardware-derived ceiling based on live CPU load). -J 1 forces strictly sequential processing. |
Info
| Option | What it does |
|---|---|
-?, --help |
Show the usage info, from which these groups are taken. |
--version |
Show version and build information. |
--list-gpus |
List this machine's Vulkan GPUs as --use-gpu matches them, then exit. |
Short options without parameters can be collapsed (-rb = -r -b). Decimal
values accept either separator (-n 2.5 = -n 2,5); printed numbers always
use ., whatever the machine's locale says.
- Pass 1 — silence scan: ffmpeg finds every silence longer than
--min-silence-length(default, and floor withauto: 1.5 s below −35 dBFS) in one quick pass. 1b. VAD pre-pass (default; skipped only with--max-jingle-length 0and no--mark-before-jingle): a bundled voice-activity model (Silero VAD) scans the whole file for speech vs. non-speech. A jingle is music, which reads as non-speech to a speech detector just like silence does — so this catches jingles the amplitude-only silence scan misses entirely: one that abuts the narration with no detectable gap on either side. Where a silence leads into the jingle, the silence scan already has it covered and VAD adds nothing redundant; VAD only contributes new candidates at the silence-less transitions, so it doesn't add extra Whisper probes for books that don't need it. - Pass 2 — probing: a short stretch of audio after each silence (and
after each silence-less jingle VAD found) is
transcribed with Whisper and matched against the chapter phrase. The chapter
number is parsed from digits, Roman numerals, or numbers written out as
words (0-999,
cardinals and ordinals alike), whether it follows the phrase ("Chapter
Seven") or precedes
it ("Erstes Kapitel", "2. Kapitel", "Birinci Bölüm"). Window borders are
snapped to silence mid-points so no decode ever cuts
a word in half, and each detection is pinpointed right away — a confident
mark even skips the remaining windows that overlap its own. By default
(
--min-silence-length auto), starting from the second mark found (the silence before the first mark is usually the intro/title silence, often longer than the breaks between chapters, so it's not used to tighten), the probing threshold sits at 75% of the shortest chapter-break silence observed so far — raised once, then only ever lowered — so shorter in-chapter pauses stop being probed once real inter-chapter breaks are established; everything skipped since the last mark is re-probed the moment a sequence gap turns up. An explicit--min-silence-lengthvalue disables this and probes every silence at or above it instead. The jingle probe window (--max-jingle-lengthplus 5 seconds for the phrase itself) self-tightens the same way by default (--max-jingle-length auto): starting from the second jingle mark found, it resizes to 1.25x the longest jingle actually observed so far, capped at the 45 s ceiling — an explicit--max-jingle-lengthvalue keeps the window fixed at it instead. A sequence gap puts the window back at the ceiling and retries every candidate since the last chapter at that width, including ones already probed while it was narrower. A number that can't be right — one leaving more than three chapters missing at once, or one below the chapters already found — is read again before it's believed: with--pass3-modelif that's the better model, then from two differently framed windows. The new reading only counts if it continues the sequence, so a book that genuinely skips numbers keeps its own. An announcement heard with no readable number at all goes down the same road, with the same rule deciding what may be believed — which is the only thing that can rescue one sitting past the last chapter found, where there's no gap for a later pass to notice. A stretch that yields nothing while VAD did hear someone speak inside its jingle is read once more from a shorter window: Whisper reads audio in 30-second chunks, and a lone announcement can drop out of a window that crosses one. 2b. Mark refinement (skip with--quick-marks/-Q): for the mark that still lands on the wrong spot — usually a jingle whose music briefly fools the voice-activity detector into sounding like speech — every mark is double-checked by re-transcribing short, isolated clips of the stretch the phrase was heard in, closing in on the announcement a few checks at a time until one hears the phrase first; its own beginning is then measured to within a tenth of a second and the mark set just ahead of it. Costs a handful of extra transcriptions per chapter, which is what--quick-markstrades away for speed. 2c. Pass 2.5 — a cheap second opinion (only with a heavier--pass3-model): most gaps aren't unprobeable audio, just a number the pass-2 model misread with the announcement sitting right there in the window. So before pass 3 commits to transcribing whole regions, the same quick probes are simply run again over the gap with the bigger model, which can close it without transcribing the region at all. Not a free bet, though: the probing cost grows with how many candidate silences the gap holds, so on a dense region it can approach the cost of the full transcription it's trying to avoid — and if it finds nothing, pass 3 still runs afterward. A gap that survives that gets one more try aimed at the other explanation, that nothing ever probed there: the pauses just short of--min-silence-lengthare swept a tenth of a second at a time, longest first, down to half a second under the setting, stopping as soon as the missing chapters turn up — which is what saves a book whose narrator's chapter break happens to land right on the limit. Only ever happens when--pass3-modelnames a better model than pass 2's, so it stays something you opt into. - Pass 3 — gap filling (only if needed): if the chapter numbers found so
far have sequence gaps, the regions where the missing chapters must be
hiding are transcribed completely, in chunks whose borders snap to
silences too (with the transcripts bridged across each seam, so not even
a phrase interrupted by a pause right at a border can slip through). Pass 3
can use a different model than pass 2 (
--pass3-model). A chapter missing after the last one found leaves no such gap — nothing above it to notice its absence — which is what--trailing-scanis for. 3b. Pass 3.5 — the shifted re-read: a gap that survives being transcribed end to end is a misreading, not unread audio — every second of it was read. The likeliest misreading is framing: Whisper decodes in 30-second windows, and an announcement landing on a window border can drop out of the transcript entirely while the sentences either side of it come through perfectly, so the text reads as though nothing were missing. Each remaining gap is therefore read once more with every decode shifted by half a window, putting whatever sat on a border as far from one as it can get. Runs unless--pass3-modelnames a lighter model than--model— the one setting that says outright the stragglers aren't worth more time — and always for--trailing-scan, where asking for the scan says the opposite. If a gap still remains after all that, the chapters that were found are written and the file is renamed with a.missing-marks-…tag listing the still-missing numbers, rather than discarded. Running the tool again over such a file resumes it automatically: the committed chapters are trusted as-is, and only the still-tagged gap(s) get their own pass 2/pass 3, exactly as after a failed--verify. The file is renamed back to its original name once every chapter is found, or re-tagged with the (possibly shorter) remaining list otherwise;--forcebypasses this and redoes the whole file from scratch. More than ten missing chapters are left out of the tag (just.missing-marks), and such a file is not resumed automatically — a gap that wide wants a look by hand first, usually starting with--max-chapter-number.
--custom "phrase:Title;..." adds marks for anything else a book announces —
an interlude, a timeline, a cast list. Such a phrase may be a /regexp/ (with
$1-style group references in the title), and is matched at any point in the
file and as often as it occurs. It must be announced, though, exactly as a
chapter phrase must: a passing mention in the narration gets no mark.
--ignore-chapter-numbers keeps chapter detection running but stops it
reasoning about the numbers — no sequence, no gaps, no missing chapters, with
whatever number was spoken still going into the title. For books that restart
their count per part, or announce chapters without numbering them at all.
Alongside the numbered chapters, a "Prologue" and an "Epilogue" mark are
detected the same way (localized by --lang, renamed with --prologue-title
and --epilogue-title, re-worded with --prologue-phrase and
--epilogue-phrase, switched off with an empty phrase). A prologue only counts
before the first numbered chapter and an epilogue only after it, at most one of
each per file.
A synthetic "Intro" mark (localized by --lang, customizable with
--intro-title) covers everything before the first detected mark
(audiobooks usually start with title/credits), so the first real chapter
keeps its exact position.
- Speed: by default (
-n auto), the silence threshold self-tightens as real chapter breaks are found, without having to guess a fixed value. If your audiobook's pauses vary too much for that to help, an explicit threshold like-n 2.5still works — far fewer Whisper probes, much faster run, but chapters go missing if it's set too high. - Jingles: by default (
-X auto), the probe window self-tightens as real jingle lengths are found, the same way-n autodoes for silences. If you know the jingle is consistently short, an explicit-X 15narrows the window and speeds things up further; if there's no jingle at all,-X 0narrows it all the way back down and skips the VAD pre-pass too. - Accuracy vs. speed:
--model turbo(default) is a good balance;largeis the most accurate and slowest. Going smaller thansmallis not advisable for real audiobooks: chapter detection stands or falls with the recognizer catching a single short phrase, andtinyin particular mishears or drops chapter announcements so often that it is supported mostly for completeness (quick experiments, toy examples). - A faster pairing worth trying:
-m small -M turbolets the small, quick model do the many short probes and keepsturboin reserve for the chapters it couldn't resolve. In testing on English and German audiobooks this came out meaningfully faster with the same chapter marks at the end — worth a--dry-runcomparison on your own books before adopting it, since it does lean on a smaller model for most of the work. - Memory: a model needs somewhat more memory to run than its download size —
whisper.cpp's own
figures are ~852 MB
for
small, ~2.1 GB formediumand ~3.9 GB forlarge, with the defaultturbolanding around 2.2 GB. (Figures quoted for OpenAI's Python implementation are two to three times higher; they do not apply here.) It comes out of video memory on a GPU backend and out of system RAM on a CPU backend, where concurrent files each load their own copy (--jobs 1keeps it to one). Nothing is checked up front; if the memory isn't there you'll hear it from the model loader, not from ABChapterize. See memory requirements for the details. - Absurd chapter numbers: if a run reports a chapter number far beyond
anything the book has (and then a long list of "missing" ones below it),
Whisper misheard a number — smaller models do this now and then. Cap it with
--max-chapter-numberset to roughly the real chapter count. - Unusual announcements:
--chapter-phraseaccepts a regexp between slashes, e.g.-c "/part (\d+)/"— a capturing group is used as the chapter number directly. - Diagnosis: run with
--verboseto see processing details as log lines, or--verbose-transcripts(-T) to also see every Whisper transcription — what the recognizer actually heard, and the confidence it had in each. Chapter marks below 50% confidence are flagged in the per-file result line (even without--verbose) as worth a manual spot-check.
Requires the .NET 10 SDK.
dotnet publish -c Release # Windows build -> bin/publish/win-x64
dotnet publish -c Release -r linux-x64 # Linux build -> bin/publish/linux-x64
dotnet test tests/ABChapterize.Tests # run the unit testsMIT. The bundled native Whisper libraries come from Whisper.net / whisper.cpp (MIT), and the speech models are OpenAI's Whisper models (MIT). The bundled jingle-detection model is Silero VAD (MIT, Copyright (c) 2020-present Silero Team) — see THIRD-PARTY-NOTICES.md for the full notice. ffmpeg is used as an external program and is not part of this project.