Metadata-Version: 2.5
Name: audiobook-import
Version: 1.1.0
Summary: Turns audiobook downloads into tagged, chaptered m4b files with Audible's metadata, and repairs books already in a library
Project-URL: Homepage, https://github.com/engelde/audiobook-import
Project-URL: Documentation, https://github.com/engelde/audiobook-import/tree/main/docs
Project-URL: Source, https://github.com/engelde/audiobook-import
Project-URL: Issues, https://github.com/engelde/audiobook-import/issues
Project-URL: Changelog, https://github.com/engelde/audiobook-import/blob/main/CHANGELOG.md
Author: David Engel
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
License-File: THIRD_PARTY_LICENSES.md
Keywords: audible,audiobook,chapters,ffmpeg,m4b,metadata,plex
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Multimedia :: Sound/Audio :: Conversion
Requires-Python: >=3.14
Requires-Dist: audible-cli>=0.3.2
Requires-Dist: audible[cryptography]>=0.13.1
Requires-Dist: moonshine-voice==0.1.5
Requires-Dist: mutagen>=1.47.0
Requires-Dist: natsort>=8.0.0
Requires-Dist: numpy>=1.24
Requires-Dist: onnx-asr[cpu]==0.12.0
Requires-Dist: python-dotenv>=1.0.0
Requires-Dist: rapidfuzz>=3.0.0
Requires-Dist: requests>=2.31.0
Provides-Extra: dev
Requires-Dist: mypy==2.4.1; extra == 'dev'
Requires-Dist: pytest-cov>=4.1; extra == 'dev'
Requires-Dist: pytest-mock>=3.12; extra == 'dev'
Requires-Dist: pytest-timeout>=2.2; extra == 'dev'
Requires-Dist: pytest>=7.4; extra == 'dev'
Requires-Dist: ruff==0.17.3; extra == 'dev'
Provides-Extra: remote
Requires-Dist: openai>=2.0.0; extra == 'remote'
Provides-Extra: review
Requires-Dist: anthropic>=1.0.0; extra == 'review'
Description-Content-Type: text/markdown

# 🎧 Audiobook Import

Turns audiobook files into tagged, chaptered `.m4b` files in a tidy library, and brings the books already in your
library up to the same standard.

- **One file per book.** A folder of MP3s, disc folders or several parts become one `.m4b` (AAC, 128 kbps by
  default). A single `.m4b` is never re-encoded.
- **Audible's metadata.** Title, author, narrator, series, description, genres and more, under the tag names Plex,
  Apple Books, Audiobookshelf, Audible's own files and Libation use, with Audible's full-size cover.
- **Audible's chapters, checked against your audio.** The same chapter names and count as in the Audible app. Each
  mark is checked against the silence before the chapter, and a local speech model hears the spoken titles to place
  marks the silences can't settle.
- **Library repair.** The same checks for books already in your library: a dry run first, and a file is only rewritten
  when the copy's audio is verified bit for bit.
- **Hands-off.** A watcher monitors a folder, imports each book that appears in it, and can refresh Plex and
  Audiobookshelf ([Media servers](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/media-servers.md)).
- **Your files are only read.** Imports work on a copy; the original folder is moved aside or kept in place.

Your library ends up like this:

```
Audiobooks/
└── Frank Herbert/
    └── Dune (1965)/
        ├── Frank Herbert - Dune (1965).m4b
        └── cover.jpg
```

## What you need

- **An Audible account**, for the metadata and chapters (any marketplace).
- **Docker with Compose, or Python 3.14 and ffmpeg 8** ([Install](#install)).
- **Memory for the speech model:** about 0.8 GB for English books, 1.4 GB for other languages
  ([Speech models](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/chapters-and-listening.md#speech-models)).

## Install

Two ways, with the same commands and settings. Docker is the simplest: its image holds everything, ffmpeg included,
for `linux/amd64` and `linux/arm64` (a 64-bit Raspberry Pi, an ARM server, Apple silicon). With Python, the commands
run directly on your computer, with an ffmpeg you install yourself.

### With Docker

**1. Get the two files**, `docker-compose.yml` and the settings file `.env`, into an empty folder, with folders for
the Audible sign-in, work files and what the program keeps:

```bash
mkdir audiobook-import && cd audiobook-import
curl -fsSLO https://raw.githubusercontent.com/engelde/audiobook-import/main/docker-compose.yml
curl -fsSL -o .env https://raw.githubusercontent.com/engelde/audiobook-import/main/.env.example
mkdir config data temp
```

**2. Fill in `.env`.** Its first section, "Docker Compose", names the folders on your computer. Set these five:

```bash
HOST_WATCH_DIR=/path/to/incoming        # the folder to watch: each book in a folder of its own
HOST_LIBRARY_DIR=/path/to/audiobooks    # your library, the folder Plex or Audiobookshelf reads
HOST_PROCESSED_DIR=/path/to/processed   # where a book's folder is moved once it is imported
HOST_TEMP_DIR=./temp                    # work files
HOST_CONFIG_DIR=./config                # the Audible sign-in
```

`./data` keeps the speech models and the repair's reports. The container runs as user 1000:1000 and must be able to
write all these folders: if they belong to someone else, set `PUID` and `PGID` to their owner (`id -u`, `id -g`).

**3. Sign in to Audible** with `docker compose run --rm signin`. Press Enter to keep each default (the profile
`audible`, the file `audible.json`, no encryption), type your marketplace's country code (`us`, `uk`, `de`…) and your
Amazon email and password, then answer `y` to the summary and enter any code Amazon asks for. The sign-in is saved as
`config/audible.json` ([The sign-in doesn't work](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/troubleshooting.md#the-sign-in-doesnt-work)).

**4. Start the watcher:**

```bash
docker compose up -d
docker compose ps                  # the watcher's state: healthy once it has started
docker compose logs -f watcher     # its log (Ctrl+C stops following it)
```

Put a book's folder in the watched folder. About a minute after its files stop changing, the book appears in the
library and its folder moves to the processed folder. A book that can't be imported stays where it is, and the log
says why and what to do ([When an import fails](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/watcher.md#when-an-import-fails)).

### With Python

It runs on Linux (x86_64 or arm64) and on macOS 15 or newer with Apple silicon; on Windows, use Docker or WSL. You
install two things yourself:

- **ffmpeg 8 or newer**, with `ffprobe`: `brew install ffmpeg` on macOS; on Linux, your distribution's package when
  `ffmpeg -version` says 8 or newer (Ubuntu 26.04, Fedora 44, Arch), else a [static build](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/installation.md#ffmpeg).
- **[uv](https://docs.astral.sh/uv/)**, which fetches Python 3.14 and installs the commands in their own environment.

```bash
uv tool install --python 3.14 --with-executables-from audible-cli audiobook-import
audible quickstart          # sign in to Audible: the same answers as in the Docker sign-in above
curl -fsSL --create-dirs -o ~/.config/audiobook-import/.env https://raw.githubusercontent.com/engelde/audiobook-import/main/.env.example
```

In that `.env`, skip the "Docker Compose" section and set `LIBRARY_DIR` (your library) and `DATA_DIR` (a folder that
keeps the speech models and saved analyses). Then import a book, or watch a folder as the Docker watcher does:

```bash
audiobook-import ~/Downloads/Dune --dry-run     # what would happen: match, chapters, tags
audiobook-import ~/Downloads/Dune               # import it into the library
audiobook-watch ~/incoming                      # import each book that appears in ~/incoming
```

[Installation](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/installation.md) has the details: libfdk_aac, pipx or a venv, the watcher as a service.

## Importing one book by hand

The `import` service runs one import with the same folders and settings. It names the book's folder by its path in
the container, where the watched folder is `/watch`; with Python, run `audiobook-import <folder>` with the same options.

```bash
docker compose run --rm import /watch/Dune --dry-run                   # what would happen: match, chapters, tags
docker compose run --rm import /watch/Dune --audible-asin B002V1OF70   # import it as this Audible book
```

`--audible-asin` names the edition when the watcher found no safe match (the log names the best candidate; the ASIN
is in the book's Audible address, `audible.com/pd/B002V1OF70`). Move the folder out of the watched folder afterwards,
or put `asin-B002V1OF70` in its name and let the watcher import it. See [Importing](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/importing.md).

## Repairing the books already in your library

Back up your library first. The `repair` service (with Python: `audiobook-repair`, with the same options) checks
every book in it; without `--apply` it changes nothing.

```bash
docker compose run --rm repair                       # dry run: report only
docker compose run --rm repair --apply --limit 5     # fix five books, then check them
docker compose run --rm repair --apply               # the rest
```

The reports go to `data/library-repair/` (with Python: `DATA_DIR/library-repair/`): every planned change, and the
books to re-download or check. The repair runs safely beside the watcher, and a run that stops continues where it left
off on the next one. See [Library repair](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/library-repair.md).

## Settings

Everything else is optional and goes in the same `.env`: Plex and Audiobookshelf, the speech model, the conversion.
`.env` explains each setting, and [Configuration](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/configuration.md) lists them all. The Python commands read it
at each start; a container when it is created: after a change, run `docker compose up -d --force-recreate`.

## Upgrading

```bash
curl -fsSLO https://raw.githubusercontent.com/engelde/audiobook-import/main/docker-compose.yml
docker compose pull
docker compose up -d
```

The first line fetches the current `docker-compose.yml` (carry over any change you made to it); `up -d` restarts the
watcher on the new image, or the version `IMAGE_TAG` in `.env` names ([Tags](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/docker.md#tags)). With Python,
`uv tool upgrade audiobook-import` upgrades the commands.

## Documentation

| Page | What's in it |
|---|---|
| [Docker](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/docker.md) | The image, its tags, folders and commands, Compose, upgrading, the health check |
| [The watcher](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/watcher.md) | Watching a folder, stability checks, processed folders, failed imports |
| [Importing](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/importing.md) | How a book's folder is read, matched, converted and tagged; exit codes |
| [Library repair](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/library-repair.md) | Checking and fixing an existing library, reports, safety |
| [Configuration](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/configuration.md) | Every setting and command-line option, with defaults |
| [Chapters and listening](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/chapters-and-listening.md) | Where chapters come from, how marks are checked, speech models |
| [Media servers](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/media-servers.md) | Refreshing Plex and Audiobookshelf after a change |
| [The review](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/review.md) | Experimental: a reasoning model checks the repair's changes |
| [Troubleshooting](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/troubleshooting.md) | The log's messages and what to do, the diagnostic scripts, questions |
| [Installation](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/installation.md) | Installing with Python: platforms, ffmpeg and its AAC encoders, the sign-in, the watcher as a service |
| [Architecture](https://github.com/engelde/audiobook-import/blob/v1.1.0/docs/architecture.md) | How the code is organized, how a book moves through it, how marks are placed |
| [Contributing](https://github.com/engelde/audiobook-import/blob/v1.1.0/CONTRIBUTING.md) | Development setup, tests, the golden comparisons, code style, releases |
| [Developer tools](https://github.com/engelde/audiobook-import/blob/v1.1.0/tools/README.md) | The comparison, development and research tools |
| [Security](https://github.com/engelde/audiobook-import/blob/v1.1.0/SECURITY.md) | Reporting a vulnerability privately; where secrets live |
| [Third-party licenses](https://github.com/engelde/audiobook-import/blob/v1.1.0/THIRD_PARTY_LICENSES.md) | The licenses of the libraries, speech models and Docker image it uses |

## Privacy

Audible credentials stay in your `audible.json`. Listening runs on your computer by default; nothing is sent anywhere.
If you choose a speech model behind an API, a few seconds of audio around each chapter mark go to that service. API
keys and the Plex and Audiobookshelf tokens are read from `.env` and never written to logs or reports. Report a
security problem privately: see [SECURITY.md](https://github.com/engelde/audiobook-import/blob/v1.1.0/SECURITY.md).

## License

Apache License 2.0: see [LICENSE](https://github.com/engelde/audiobook-import/blob/v1.1.0/LICENSE). Copyright 2025 David Engel.

Some libraries it installs have licenses of their own: `audible` and `audible-cli` (Audible access) are AGPL-3.0 and
`mutagen` (tags) is GPL-2.0-or-later. They aren't part of this repository, but anyone who distributes the program
together with them, as the Docker image does, must follow their terms too. The
[third-party licenses](https://github.com/engelde/audiobook-import/blob/v1.1.0/THIRD_PARTY_LICENSES.md) page lists every library, speech model and the Docker image's ffmpeg
with its license, and what the AGPL and GPL mean here.

This project is not affiliated with Audible or Amazon. Use it with audiobooks you own.
