Creates .m4b audiobooks from .mp3, .m4a and .m4b files using ffmpeg, with chapters, metadata and cover art.
- Zero dependencies (except, of course, for
ffmpeg) - Remuxes AAC sources instead of re-encoding them, so
.m4a/.m4binput is fast and lossless - Chapters, tags and cover art written in a single ffmpeg pass
- Progress with elapsed time and speed
- Per-job logs kept beside the book
Requires Python 3.13+ and ffmpeg/ffprobe on your PATH.
git clone https://github.com/pmsoltani/m4bmaker.git
cd m4bmaker
uv syncThree steps: generate a config, check it, convert it.
m4bmaker scaffold "/path/to/book" # writes <book>/m4bmaker.json
m4bmaker inspect "/path/to/book" # review what it will produce
m4bmaker convert "/path/to/book"scaffold sorts files naturally (Ch 2 before Ch 10) and guesses a layout, so you can edit the JSON before converting. Chain the steps once you trust the result:
m4bmaker scaffold "/path/to/book" && m4bmaker convert "/path/to/book"| command | purpose |
|---|---|
scaffold BOOK |
Write a starter m4bmaker.json. --layout single|chapter, --dry-run, --force |
inspect BOOK |
Show tracks, chapters and durations. --json for raw data |
convert BOOK |
Build the .m4b files into <book>/output/. --force to overwrite. --re-encode to encode AAC sources instead of remuxing |
--layout single makes one .m4b with a chapter per file; --layout chapter makes one .m4b per file.
scaffold writes this for you; see m4bmaker.example.json for every field.
{
"path": "~/path/to/your/audio/files",
"title": "Audiobook Title",
"author": "Author",
"narrator": "Narrator",
"genre": "Audiobook",
"year": "2025",
"cover": "cover.jpg",
"tracks": [
{
"title": "Track 1",
"file": "01 Track 1.m4b",
"chapters": [
{ "title": "Chapter 1", "files": ["file1.mp3", "file2.mp3"] },
{ "title": "Chapter 2", "files": ["file3.mp3"] }
]
}
]
}Every field except path and tracks is optional. title falls back to the directory name, track and chapter titles are generated when blank, author maps to Artist and Album Artist, and narrator to Composer. All input files must share one format.
from pathlib import Path
from m4bmaker import M4BMaker
m4b = M4BMaker(json_path=Path("~/audiobooks/my-book/m4bmaker.json").expanduser())
print(m4b.inspect())
m4b.convert()uv sync
uv run pytest # 59 tests; the slow ones need ffmpeg
uv run ruff check .
uv run ty check m4bmaker testsMIT.