Do you have an ever-growing list of books to read, but can't stand the tedium of buying, downloading and sending to device? Well, not anymore.
Automated reading list to e-reader pipeline. Watches an Obsidian markdown reading list, resolves book metadata, downloads EPUBs from online book collections, adds them to Calibre, and emails them to your e-reader. Available as a CLI or a desktop GUI.
Reading list (.md) → Parse entries → Resolve metadata (Google Books / Open Library)
→ Search book collections → Download EPUB → Validate → Add to Calibre → Email to e-reader
Each book is tracked through these statuses:
pending → resolved → downloading → downloaded → in_calibre → sending → sent
↘ failed
State lives in SQLite at ~/.booker/booker.db. The reading list is never modified.
Download the latest release for your platform from Releases. Available for macOS and Windows.
Requires Python 3.12+ and Calibre installed at /Applications/calibre.app (optional — can be disabled).
git clone https://github.com/nenadom/booker.git && cd booker
python3 -m venv .venv
source .venv/bin/activate
# CLI only
pip install -e .
# CLI + GUI
pip install -e ".[gui]"Create ~/.booker/.env:
KINDLE_EMAIL=you@example.com
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=you@gmail.com
SMTP_PASSWORD=xxxx xxxx xxxx xxxx
The SMTP password is a Gmail App Password (requires 2FA). Your Gmail address must be approved in your e-reader's email settings (e.g. for Kindle: Amazon's Approved Personal Document Email List).
GUI users can configure all settings from the Settings tab instead of editing .env manually.
# Preview what the parser sees (no DB writes)
booker parse
# Dry run — parse + resolve metadata, no downloads
booker run --dry-run
# Single full pipeline run
booker run --once
# Continuous mode (runs every 2 hours)
booker run
# Check book statuses
booker status
booker status --filter=failed
# Re-queue a failed book
booker retry <id>
# Skip a book permanently
booker skip <id># Install launchd plist (runs every 2 hours)
booker service install
# Check logs
booker service logs
# Remove service
booker service uninstallLaunch the desktop interface:
booker-gui
# or
python -m booker.guiThe GUI has four tabs:
- Reading List — view and edit your reading list markdown file
- Status — run the pipeline, monitor progress, and manage book statuses (retry, skip)
- Settings — configure all options (paths, email, Calibre, run interval) without editing
.env - Logs — live-scrolling pipeline log output
The app minimizes to the system tray on close. A welcome tutorial walks through first-time setup on launch.
Booker parses Obsidian checklist entries in many formats:
- [ ] Deep Learning, Ian Goodfellow
- [ ] _Piranesi_ by Susanna Clarke
- [ ] the listeners - jordan tannahill
- [ ] thinking in systems
- [ ] [how to take over the world](https://amazon.com/.../059319201X)
- [x] the algebra of wealth, scott galloway ← skipped (done)
- [ ] iain banks culture series ← flagged for manual reviewAmazon ASINs and Goodreads IDs are extracted from URLs. Title/author swaps are detected and corrected during metadata resolution.
All config is via environment variables in ~/.booker/.env:
| Variable | Default | Description |
|---|---|---|
KINDLE_EMAIL |
— | Your e-reader email address |
SMTP_HOST |
smtp.gmail.com |
SMTP server |
SMTP_PORT |
587 |
SMTP port |
SMTP_USER |
— | Gmail address |
SMTP_PASSWORD |
— | Gmail App Password |
READING_LIST_PATH |
~/.booker/reading_list.md |
Path to reading list |
DOWNLOAD_DIR |
~/Books |
Where EPUBs are saved |
CALIBRE_LIBRARY |
~/Calibre Library |
Calibre library path |
CALIBRE_ENABLED |
true |
Set to false to skip Calibre import |
RUN_INTERVAL_MINUTES |
120 |
Minutes between pipeline runs in continuous/GUI mode |
src/booker/
├── cli.py # Click CLI commands
├── config.py # Environment and path configuration
├── models.py # BookStatus enum, ParsedEntry, ResolvedBook, BookRecord
├── parser.py # Obsidian markdown → ParsedEntry list
├── resolver.py # Google Books API + Open Library fallback
├── downloader.py # Book collection search + multi-mirror EPUB download
├── validator.py # EPUB integrity checks (ZIP, mimetype, size, traversal)
├── calibre.py # calibredb add wrapper
├── sender.py # Gmail SMTP to e-reader email
├── notifier.py # macOS notifications via osascript
├── pipeline.py # Orchestrator tying all steps together
├── db.py # SQLite schema + CRUD
└── gui/
├── app.py # QApplication entry point
├── main_window.py # Tabbed main window + system tray
├── editor_view.py # Reading list editor tab
├── status_view.py # Pipeline status + controls tab
├── settings_view.py # Configuration form tab
├── log_view.py # Live log viewer tab
├── scheduler.py # Periodic pipeline runner
├── workers.py # QThread workers for pipeline execution
├── tray.py # System tray icon + menu
└── welcome.py # First-run tutorial dialog
pytest60 tests covering parsing, metadata resolution, EPUB validation, retry logic, concurrent DB access, and file deduplication.