A CLI-driven audiobook library manager for Audible, built in Go. Earworm downloads new books from Audible via audible-cli, organizes them into an Audiobookshelf-compatible folder structure, and triggers library scans -- all from a single binary.
- SQLite-backed library state tracking
- Audible library sync via audible-cli
- Fault-tolerant batch downloads with rate limiting and crash recovery
- Automatic organization into
Author/Title [ASIN]/folder structure - Deep library scanning with structural issue detection
- Plan-based cleanup workflow (review before applying)
- CSV import for bulk operations with flexible column names and metadata
- Multi-book folder detection and splitting
- Skip management for unwanted books
- Audiobookshelf library scan integration
- Goodreads CSV export
- Daemon/polling mode for unattended operation
- Cross-filesystem file moves (local to NAS)
- Contextual next-step hints after each command (suppressed with
--quiet)
- Go 1.23+ (building from source)
- Python 3.9+ (required by audible-cli)
Note: audible-cli is installed automatically into an embedded Python venv on first use. You do not need to install it manually.
go install github.com/lovettbarron/earworm/cmd/earworm@latestDownload the latest binary for your platform from the GitHub Releases page.
git clone https://github.com/lovettbarron/earworm.git
cd earworm
go build -o earworm ./cmd/earwormearworm version# 1. Install earworm
go install github.com/lovettbarron/earworm/cmd/earworm@latest
# 2. Initialize configuration
earworm config init
# 3. Set your library path (where audiobooks are stored)
earworm config set library_path /path/to/audiobooks
# 4. Authenticate with Audible
earworm auth
# 5. Sync your Audible library
earworm sync
# 6. Preview what would be downloaded
earworm download --dry-run
# 7. Download new books
earworm download
# 8. Organize into library structure
earworm organizeThese flags are available on all commands:
| Flag | Description |
|---|---|
--config <path> |
Use a custom config file path |
--quiet / -q |
Suppress non-essential output |
Authenticate with Audible via audible-cli. This is an interactive process -- you will be prompted for your Audible credentials directly by audible-cli.
earworm authSync Audible library metadata to the local database. Each sync is a full refresh -- all books are upserted. Local-only data (download status, file paths) is preserved.
Pre-ordered books that were previously marked unavailable (because they weren't yet released when a download was attempted) are automatically reset on re-sync, so they become downloadable once released. Books confirmed unavailable 3 or more times (e.g. expired subscriptions) stay unavailable and won't be retried.
earworm sync
earworm sync --json| Flag | Description |
|---|---|
--json |
Output sync summary in JSON format |
Scan a local library directory for existing audiobooks. The library is expected to follow the structure: Author Name/Book Title [ASIN]/book.m4a.
earworm scan
earworm scan --recursive
earworm scan --deep # Also detect structural issues
earworm scan --deep --json| Flag | Description |
|---|---|
--recursive / -r |
Recursively scan nested directories |
--deep |
Scan all folders including those without ASINs and detect issues |
--json |
Output in JSON format (only with --deep) |
List issues detected by the last earworm scan --deep run.
earworm scan issues
earworm scan issues --type nested_audio
earworm scan issues --create-plan| Flag | Description |
|---|---|
--json |
Output in JSON format |
--type <type> |
Filter issues by type (e.g., nested_audio, empty_dir, missing_metadata) |
--create-plan |
Create a remediation plan from actionable issues |
Display the current state of your audiobook library including book metadata and download status.
earworm status
earworm status --author "Sanderson"
earworm status --status downloaded --json| Flag | Description |
|---|---|
--json |
Output in JSON format |
--author <name> |
Filter by author (substring match) |
--status <status> |
Filter by status (exact match: scanned, downloaded, organized, error) |
Download audiobooks that are in your Audible library but not yet downloaded locally. Includes rate limiting, exponential backoff, and crash recovery.
earworm download
earworm download --dry-run
earworm download --limit 5
earworm download --asin B08G9PRS1K --asin B09FKZQ843| Flag | Description |
|---|---|
--dry-run |
Preview downloads without downloading |
--json |
Output in JSON format (dry-run mode) |
--limit <N> |
Maximum number of books to download (0 = no limit) |
--asin <ASIN> |
Download specific books by ASIN (repeatable) |
Signal handling: Press Ctrl+C once to finish the current book and stop. Press Ctrl+C twice to force exit immediately.
Move downloaded audiobooks from the staging directory into the library in Audiobookshelf-compatible Author/Title [ASIN]/ folder structure. Operates on all books with downloaded status.
earworm organize
earworm organize --json| Flag | Description |
|---|---|
--json |
Output results in JSON format |
Trigger an Audiobookshelf library scan via the API. Requires Audiobookshelf configuration (see Audiobookshelf Integration).
earworm notify
earworm notify --json| Flag | Description |
|---|---|
--json |
Output result in JSON format |
Export your library to a Goodreads-compatible CSV file for import into your Goodreads shelves.
earworm goodreads -o library.csv
earworm goodreads --output ~/exports/earworm.csv| Flag | Description |
|---|---|
--output / -o |
Output file path (required) |
Run earworm in polling mode for unattended operation. Periodically runs the full sync, download, organize, and notify cycle.
earworm daemon
earworm daemon --interval 4h
earworm daemon --once --verbose| Flag | Description |
|---|---|
--interval <duration> |
Polling interval (default: 6h) |
--verbose |
Enable verbose logging |
--once |
Run one cycle and exit |
Mark books as skipped so they are excluded from future downloads. Use for subscription books you no longer have access to, or books you don't want.
earworm skip B08G9PRS1K
earworm skip B08G9PRS1K B09FKZQ843
earworm skip B08G9PRS1K --undo # Un-skip, make downloadable again| Flag | Description |
|---|---|
--undo |
Un-skip books (mark as unknown again) |
Manage library cleanup plans. Plans contain a set of operations (move, flatten, delete, write_metadata) that can be reviewed before applying.
earworm plan list
earworm plan list --status draft
earworm plan review 5
earworm plan apply 5 # Dry-run by default
earworm plan apply 5 --confirm # Actually apply
earworm plan import operations.csv
earworm plan approve 5| Flag | Description |
|---|---|
--json |
Output in JSON format |
--status <status> |
Filter by plan status |
Review a plan's operations before applying.
| Flag | Description |
|---|---|
--json |
Output in JSON format |
Apply a plan's operations. Dry-run by default -- use --confirm to actually apply.
| Flag | Description |
|---|---|
--confirm |
Actually apply the plan (default is dry-run preview) |
--json |
Output in JSON format |
Import a plan from a CSV file. The CSV must have columns for operation type, source path, and optionally destination path. Column names are flexible -- common aliases are accepted:
| Canonical | Accepted aliases |
|---|---|
op_type |
type, operation, action |
source_path |
source, path, src, current_path |
dest_path |
destination, dest, target |
Metadata columns (title, author, narrator, genre, year, series, asin) are extracted as JSON and attached to operations for write_metadata use.
| Flag | Description |
|---|---|
--name <name> |
Plan name (defaults to filename without extension) |
--json |
Output in JSON format |
Transition a draft plan to ready status so it can be applied.
| Flag | Description |
|---|---|
--json |
Output in JSON format |
Process delete operations from completed plans by moving files to a trash directory. Requires double confirmation before any files are moved.
earworm cleanup
earworm cleanup --plan-id 5
earworm cleanup --permanent # DANGEROUS: permanently deletes| Flag | Description |
|---|---|
--plan-id <id> |
Only process deletes from this plan |
--permanent |
Permanently delete instead of moving to trash (dangerous) |
--json |
Output in JSON format |
Detect and split multi-book folders (folders containing audio files from multiple audiobooks).
Detect book groupings in a multi-book folder.
earworm split detect /path/to/multi-book-folder
earworm split detect /path/to/multi-book-folder --json| Flag | Description |
|---|---|
--json |
Output in JSON format |
Create a split plan for a multi-book folder. Run detect first to preview groupings.
earworm split plan /path/to/multi-book-folder| Flag | Description |
|---|---|
--json |
Output in JSON format |
Create the default configuration file at ~/.config/earworm/config.yaml.
earworm config initDisplay the current configuration with all values.
earworm config showUpdate a configuration setting.
earworm config set library_path /mnt/nas/audiobooks
earworm config set download.rate_limit_seconds 10
earworm config set audiobookshelf.url http://nas:13378Display build version, commit hash, and build date.
earworm versionConfig file location: ~/.config/earworm/config.yaml
| Key | Default | Description |
|---|---|---|
library_path |
(none) | Path to audiobook library -- can be a NAS mount (required) |
staging_path |
~/.config/earworm/staging |
Temporary download directory |
audible_cli_path |
audible |
Path to audible-cli binary (default uses managed venv) |
audible.profile_path |
(none) | Path to audible-cli profile directory |
audiobookshelf.url |
(none) | Audiobookshelf server URL |
audiobookshelf.token |
(none) | Audiobookshelf API token |
audiobookshelf.library_id |
(none) | Audiobookshelf library ID |
daemon.polling_interval |
6h |
Polling interval for daemon mode |
download.rate_limit_seconds |
5 |
Seconds between download requests |
download.max_retries |
3 |
Maximum retry attempts per book |
download.backoff_multiplier |
2.0 |
Exponential backoff multiplier for retries |
scan.recursive |
false |
Scan subdirectories recursively |
Earworm can trigger an Audiobookshelf library scan after organizing downloads, so new books appear automatically in your media server.
-
Get your API token: In Audiobookshelf, go to Settings > Users > (your user) > API Token. Copy the token.
-
Get your library ID: Check the URL when viewing your library in Audiobookshelf -- the ID is in the path (e.g.,
http://nas:13378/library/abc123-- the ID isabc123). -
Configure earworm:
# ~/.config/earworm/config.yaml
audiobookshelf:
url: http://your-server:13378
token: your-api-token
library_id: your-library-idOr via CLI:
earworm config set audiobookshelf.url http://your-server:13378
earworm config set audiobookshelf.token your-api-token
earworm config set audiobookshelf.library_id your-library-idAfter downloads are organized, trigger a scan manually:
earworm notifyIn daemon mode, the scan is triggered automatically after each organize cycle.
Run earworm in the background for fully unattended audiobook management. The daemon runs a full cycle (sync, download, organize, notify) at a configurable interval.
earworm daemon # Poll every 6 hours (default)
earworm daemon --interval 4h # Poll every 4 hours
earworm daemon --once # Run one cycle and exitCreate /etc/systemd/system/earworm.service:
[Unit]
Description=Earworm audiobook library manager
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=your-user
ExecStart=/usr/local/bin/earworm daemon
Restart=on-failure
RestartSec=60
[Install]
WantedBy=multi-user.targetEnable and start:
sudo systemctl enable earworm
sudo systemctl start earworm
sudo systemctl status earwormCreate a plist at ~/Library/LaunchAgents/com.earworm.daemon.plist with the earworm daemon command. Load with launchctl load.
Export your audiobook library to a CSV file compatible with Goodreads import.
earworm goodreads -o library.csvThen import the CSV at goodreads.com/review/import. Books are placed on the "read" shelf.
- Configuration:
~/.config/earworm/config.yaml - Database:
~/.config/earworm/earworm.db(SQLite, always local -- never on NAS) - Staging:
~/.config/earworm/staging/(temporary download directory)
The database stores library state. It is safe to delete and will be recreated on next scan or sync.
MIT License. See LICENSE for details.