Why the chapter list is empty
An m4b file can carry chapters in two places: the Nero chpl atom (a flat list of titles and start times) or a QuickTime chapter track (a text track referenced by tref/chap). Most tools write one or both. Some sellers strip them, and some converters drop them on the way from mp3 to m4b. If neither is present, no player can show chapters, because there is nothing in the file to show.
Kithara reads both layouts, plus ID3 CHAP frames in mp3s and .cue sheets. So once the chapters are in the file, they will appear. Everything below uses ffmpeg, which is free, runs on Windows, macOS and Linux, and ships with ffprobe.
1. Check what is already there
ffprobe -v error -show_chapters -of compact=p=0:nk=1 book.m4b
One line per chapter means the chapters exist and the problem is elsewhere (see the last section). No output means the file has none. While you are here, grab the total length; you will need it for the last chapter's end time:
ffprobe -v error -show_entries format=duration -of csv=p=0 book.m4b
That prints seconds with decimals, for example 39600.128.
2. Write a chapter file
ffmpeg takes chapters from a small text file in its ffmetadata format. Times are in the unit set by TIMEBASE; 1/1000 means milliseconds, which is easiest to reason about. Save this as chapters.txt next to the audio:
;FFMETADATA1
title=The Hobbit
artist=J. R. R. Tolkien
album_artist=Rob Inglis
[CHAPTER]
TIMEBASE=1/1000
START=0
END=2760000
title=An Unexpected Party
[CHAPTER]
TIMEBASE=1/1000
START=2760000
END=5100000
title=Roast Mutton
[CHAPTER]
TIMEBASE=1/1000
START=5100000
END=39600128
title=A Short Rest
Rules that trip people up:
- The first line must be exactly
;FFMETADATA1. - Each chapter's
STARTshould equal the previous chapter'sEND. Gaps and overlaps produce odd behaviour in some players. - The last
ENDis the file's total duration in milliseconds (the seconds figure from step 1, times 1000, rounded). - Titles are plain text. If a title contains
=,;,#or\, escape it with a backslash. - The tag lines at the top are optional. Leave them out and the file's existing tags are kept.
Where do the timestamps come from? If you have a printed chapter list with running times, convert each to milliseconds. If you are merging a folder of files (next section), each file's duration gives you the boundaries. If you have nothing, scrub through the book in any player and note where chapters start; a handful of minutes of work for a book you will listen to for thirty hours.
3. Apply the chapters without re-encoding
ffmpeg -i book.m4b -i chapters.txt -map_metadata 1 -map_chapters 1 -c copy book-chaptered.m4b
What each part does: the audio is input 0 and the chapter file is input 1; -map_chapters 1 takes the chapter list from input 1; -map_metadata 1 takes the tags from it too (drop this flag to keep the original tags exactly); -c copy copies the audio stream untouched, so the operation takes seconds and loses no quality. ffmpeg's MP4 muxer writes both the chpl atom and a QuickTime chapter track, so the result works in Kithara and in every other player that reads either layout.
If the original had cover art embedded, it is carried across. If it did not, add one at the same time:
ffmpeg -i book.m4b -i chapters.txt -i cover.jpg -map 0:a -map 2 -map_metadata 1 -map_chapters 1 -c copy -disposition:v attached_pic book-chaptered.m4b
4. Verify, then replace
ffprobe -v error -show_chapters -of compact=p=0:nk=1 book-chaptered.m4b
You should see one line per chapter with the titles you wrote. Play the new file for a moment, then replace the original. On the phone, rescan the folder; Kithara identifies books by where they live, so your listening position survives the swap.
Merging a folder of mp3s into one m4b
You do not need to do this for Kithara: a folder of numbered files already plays as one book on one timeline, and each file becomes a chapter. But a single m4b is tidier to store and share, and it lets you give the chapters real names. This step re-encodes the audio, so it takes a while and you should pick a bitrate you are happy with (64 kbps AAC is plenty for a single narrator).
First, list the files in order. ffmpeg's concat demuxer reads a plain text list:
file 'Part 01.mp3'
file 'Part 02.mp3'
file 'Part 03.mp3'
Get each file's duration in seconds to build the chapter boundaries:
ffprobe -v error -show_entries format=duration -of csv=p=0 "Part 01.mp3"
Write chapters.txt as in step 2, with one chapter per file (chapter 1 starts at 0 and ends at the first file's length; chapter 2 starts there; and so on). Then merge, encode and chapter in one go:
ffmpeg -f concat -safe 0 -i list.txt -i chapters.txt -map_metadata 1 -map_chapters 1 -c:a aac -b:a 64k -vn book.m4b
For dozens of books, m4b-tool automates exactly this (durations, chapter file, merge, tags) and is worth learning once.
The no-re-mux alternative: a cue sheet
If you would rather not touch the audio at all, Kithara will read chapters from a .cue file sitting next to a single audio file. The cue must have the same base name as the audio (book.m4b and book.cue, case does not matter), and only three lines per track are read: TRACK, TITLE and INDEX 01.
TITLE "The Hobbit"
PERFORMER "Rob Inglis"
FILE "book.m4b" MP4
TRACK 01 AUDIO
TITLE "An Unexpected Party"
INDEX 01 00:00:00
TRACK 02 AUDIO
TITLE "Roast Mutton"
INDEX 01 46:00:00
TRACK 03 AUDIO
TITLE "A Short Rest"
INDEX 01 85:00:00
Times are minutes:seconds:frames with 75 frames per second, the CD convention, and minutes can exceed 59 (85:00:00 is one hour twenty-five). The cue is used only when the file itself has no chapters, so it is a fix for stripped files, not an override. Other players vary in whether they read cue sheets, which is why embedding is the better long-term answer.
Chapters exist but still do not show
- Multi-file book, no chapter names. Kithara makes one chapter per file when the files carry no marks. To get names, either tag each mp3 with a title (ffmpeg's
-metadata title=on each file) or merge into an m4b as above. - Streaming from a server. Kithara does not re-parse a streamed file over the network; it uses the chapter list the server sends. Audiobookshelf reads embedded chapters when it scans, so fix the file, then rescan the item on the server.
- The mp3 has ID3v2.2 chapters. Only v2.3 and v2.4
CHAPframes are read. Re-save the tags with a modern tagger such as Mp3tag or Kid3. - Every chapter is called "Chapter 1". Some tools write the chapter track with empty or duplicated titles. Re-run step 3 with a proper
chapters.txt; ffmpeg replaces the old list.
Still stuck? adb logcat -s Mp4Parser Id3ChapterReader MediaScanner while rescanning prints exactly what Kithara found in the file, and we are happy to look at a log.