Browse a downloaded audiobook collection in your web browser: a filterable list of
<author> - <title> beside a preview pane of cover art and book details fetched from
Audible/Audnexus, with Open Library as a fallback.
Requires Python 3.11 or newer.
Create a virtual environment and install the package in editable mode with its dev dependencies:
python -m venv .venv
.venv\Scripts\pip install -e ".[dev]" # Windows
.venv/bin/pip install -e ".[dev]" # LinuxIf you have uv installed, uv sync is an equivalent
alternative to creating the venv and installing dependencies above.
.venv\Scripts\audiobook-browser --root "D:\Audiobooks" # Windows
.venv/bin/audiobook-browser --root /mnt/media/Audiobooks # Linux(With uv: uv run audiobook-browser --root ... instead.)
The root can also come from AUDIOBOOK_ROOT, or from root = "..." in the config file
printed by the error message when none is set.
Options: --port (default 8473), --no-open, --audible-region (default us),
--concurrency (default 4).
The first run lists every book immediately from folder names and fills in details in the background — several minutes for a large collection. Later runs only fetch new books.
Details come from Audible (enriched via Audnexus), falling back to Open Library for books Audible does not stock. A book the app is unsure about carries a check badge; one it could not match at all carries no match and shows its folder-derived title rather than a wrong candidate. Two ways to correct it, both in the preview pane:
- Fix match searches for alternatives and pins the one you pick.
- Confirm match accepts the candidate already on screen, for when a low-confidence match is simply right. It appears only when accepting would actually gain something.
- Enter details by hand takes a description you paste from anywhere, optional comma-separated genres, and optionally a cover image URL. Use this for books in no catalogue the app can reach — some audiobooks are absent from Audible in every marketplace and from Open Library too. The genres matter: without them the book cannot be reached by the genre dropdown. Leave the cover URL blank to keep the cached image; paste the address of an image file to replace it, which is how you fix a wrong match's artwork after correcting everything else.
Both are pinned, so a rescan or a later fetch never overwrites them.
Metadata is cached in <root>/.cache: one JSON file per book under books/, downloaded
covers under covers/. Delete a book's JSON file to force a re-fetch. Nothing outside
.cache is ever written.
A book cached as unmatched is deliberately never retried — that verdict means the
search succeeded and found nothing, so repeating it would waste a request every start.
If the matching logic changes, delete those entries to have them reconsidered.
Entries are named after a hash of the book's path relative to the root, so moving a book
— into _on_phone, say — or renaming its folder strands the old entry rather than
updating it, and the book is looked up again under its new path. Nothing removes stranded
entries on its own:
.venv\Scripts\audiobook-browser --root "D:\Audiobooks" --prune --dry-run # list them
.venv\Scripts\audiobook-browser --root "D:\Audiobooks" --prune # remove themBoth exit without starting the server. Pinned entries are kept even when stranded: they
hold details you confirmed or typed in, and a book moved aside today may come back
tomorrow to the same path, at which point its entry is picked straight back up. They are
listed so you can delete them by hand if those books are gone for good. Pruning refuses
to run if the scan finds no books at all, so a wrong --root or an unmounted drive
cannot empty the cache.
Released into the public domain under The Unlicense. Do whatever you like with it.
.venv\Scripts\python -m pytest # Windows, offline
.venv\Scripts\python -m pytest -m network # Windows, also hits the real APIs
.venv/bin/python -m pytest # Linux, offline
.venv/bin/python -m pytest -m network # Linux, also hits the real APIs(With uv: uv run pytest / uv run pytest -m network instead.)