# Book To Audiobook

> Convert an epub/pdf/txt book into a narrated audiobook in any language, using edge-tts. Use when the user asks to "turn this book into an audiobook", "narrate this epub", or similar — for any book conversion request in this repository.

- **Type:** Skill
- **Install:** `agentstack add skill-gomesfellipe-book-to-audiobook-book-to-audiobook`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [gomesfellipe](https://agentstack.voostack.com/s/gomesfellipe)
- **Installs:** 0
- **Category:** [Content & Media](https://agentstack.voostack.com/c/content-and-media)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [gomesfellipe](https://github.com/gomesfellipe)
- **Source:** https://github.com/gomesfellipe/book-to-audiobook/tree/main/.claude/skills/book-to-audiobook

## Install

```sh
agentstack add skill-gomesfellipe-book-to-audiobook-book-to-audiobook
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Book to Audiobook

Converts a book file into an MP3 audiobook using free, keyless services
(`edge-tts` for speech, `deep-translator`'s `GoogleTranslator` for optional
translation — both need internet, neither needs an API key).

All operational choices (source language, target language, voice, speed,
input file, output path) are asked in this conversation, every time. Never
read from `.env` — there is nothing to configure there.

This skill is a thin conversational wrapper around `python -m src.cli`. All
actual text extraction, boundary detection, and audio synthesis is
deterministic code in `src/` — don't reimplement it, call the CLI via Bash.

## Gates

Work through these in order. Each gate ends with an explicit stop-and-wait
for the user — do not skip ahead, even if a step "looks obvious" (this
applies especially to Gate 3, Gate 5, and Gate 6, see Common Mistakes
below).

### Gate 0 — Setup

Run `python -m src.cli doctor`. If it reports missing dependencies, install
them (`pip install -r requirements.txt`) or tell the user to install
`ffmpeg` (`brew install ffmpeg` / `apt install ffmpeg`). First run can be
slow while dependencies install — say so, so it doesn't read as a hang.

### Gate 1 — Book and preferences

Ask the user for the following, **every time**, even if the original request
already implied one of them (e.g. "in Portuguese") — restate and confirm,
never infer or skip. Everything the user answers here is passed on as an
explicit parameter; nothing downstream in the pipeline infers or
auto-detects anything:

1. The book's name (not full path — look for it inside `books/private/`).
2. The book's source language (the language the text is already in).
3. Target language, or "no translation".
4. Voice — after (2)/(3) are known, run
   `python -m src.cli voices --lang  --json` using the relevant
   language (the target language if translating, otherwise the source
   language) and propose a sensible Neural voice from the real results for
   the user to confirm or swap. There is no hardcoded regional default, but
   this isn't a blind pick either — it's always grounded in a real query
   against the actual voice catalog.
5. Playback speed — ask as an explicit choice, not an open-ended question:
   **1x** (`+0%`, default), **1.5x** (`+50%`), **2x** (`+100%`), or **3x**
   (`+200%`). If the user wants something else, accept a custom edge-tts
   rate string. Never assume 1x without asking.

### Gate 2 — Inspect

Run `python -m src.cli inspect  --json`. Parse the JSON: metadata
(title/author/subtitle) and `boundary_candidates`.

Text extraction automatically strips decorative separator lines (rows of a
single repeated symbol used as scene breaks, e.g. `*      *      *      *`
or `-----` or `~ ~ ~`) and rewrites roman-numeral chapter numbers to arabic
digits (`CHAPTER IV` → `CHAPTER 4`, `CAPÍTULO XII` → `CAPÍTULO 12`) so
edge-tts pronounces them naturally instead of misreading them. This is
automatic and needs no confirmation — it's mechanical text hygiene, not a
judgment call. Report the counts from the JSON's `cleaning` field to the
user as an FYI (e.g. "removed 6 decorative lines, converted 12 chapter
numbers to digits").

### Gate 3 — Confirm the start/end cut

Print the actual `preview` text as visible prose in the chat — not just
summarized inside an AskUserQuestion option — for both the candidate you're
proposing as the start and the one you're proposing as the end. Show enough
surrounding text that the user can see whether a preface/foreword/
introduction sits between the proposed start and the first chapter.

Before asking for confirmation, explicitly check whether any
`boundary_candidates` entry has `"kind": "front_matter"` between the
proposed start and the first `chapter_heading`:
- **None found**: say so explicitly — "No preface/foreword/introduction
  detected between the start and the first chapter."
- **One or more found**: show its `preview` text and ask explicitly
  whether to keep it in the audiobook or skip straight to the first
  chapter — never decide this silently either way.

**Wait for explicit confirmation of `--start-char`/`--end-char` before
proceeding.** This is the core value of the skill — the semantic judgment
of where real content starts/ends (table of contents, dedication, preface
at the start; afterword, ads for other books at the end) that today only a
human makes. Gutenberg markers, when present, are one heuristic among
five — not an automatic answer.

### Gate 4 — Confirm title/author/subtitle

Show the metadata from Gate 2's JSON. Ask explicitly for confirmation or
correction (e.g. via AskUserQuestion) — never proceed on silence or
assumption.

### Gate 5 — Translate (if requested)

If Gate 1 chose a target language, run:

```
python -m src.cli pronunciation  --start-char N --end-char M \
  --source-lang  --translate-to  --json
```

(using the offsets confirmed in Gate 3). This translates the trimmed text
and caches it — the exact same cache `convert` reads from in Gate 7, so
translation only ever happens once. Read the resulting cache file
`.cache/.-..txt` and show the user the
actual translated prose (not just a summary) so they can judge translation
quality on its own — separately from pronunciation. **Wait for explicit
confirmation before moving to Gate 6.**

Keep this JSON response — Gate 6 reads its `flagged_tokens` without
needing a second CLI call. If no translation was requested, skip this gate
entirely and go straight to Gate 6.

### Gate 6 — Review pronunciation

**If Gate 5 ran (translation requested): do not call `pronunciation` again —
reuse its JSON response.** That response already flags pronunciation-risky
tokens **in the translated text itself**, since that's what actually gets
spoken — reviewing the original-language text would miss whatever the
translation introduced or changed. For reference, the response you're
reusing came from:

```
python -m src.cli pronunciation  --start-char N --end-char M \
  --source-lang  --translate-to  --json
```

**If no translation was requested**, this gate makes its own first
`pronunciation` call — the same command *without* `--source-lang`/
`--translate-to` — which flags tokens in the original text instead:

```
python -m src.cli pronunciation  --start-char N --end-char M --json
```

Show the user the flagged tokens (acronyms, numbers, foreign names) with
context from `flagged_tokens`. Let them correct spellings that the TTS
voice would mispronounce:

- **Translated text**: edit the cache file directly at
  `.cache/.-..txt`. It's a flat
  translated string nothing else indexes into, so editing it is safe — no
  offsets to recompute afterward.
- **Original text (no translation, or fixing something translation won't
  touch)**: edit `.cache/.txt` instead. This *does* shift character
  offsets if the edit changes text length anywhere before `end_char` — after
  editing, re-run `inspect` and reconfirm `--start-char`/`--end-char` (Gate
  3) rather than reusing the old numbers.

This is a **text** review, not a phonetic dictionary — same pattern as
`pitch-video-studio`.

### Gate 7 — Convert

Only after Gates 1-6 are confirmed. If translation was requested, Gate 5
already populated the translation cache, so this step is just audio
synthesis — no re-translation, no extra wait:

```
python -m src.cli convert  \
  --start-char  --end-char  \
  --voice  --source-lang  --rate  [--translate-to ] \
  --output 
```

### Gate 8 — Review the result

Report the output file's size/duration to the user. `convert` prints its own
`[WARN]` line if the output audio looks truncated relative to the expected
word-count-based duration — surface that warning to the user verbatim if it
appears, don't just report size/duration as if everything were fine. Ask
them to listen and confirm it sounds right.

## Common Mistakes

- Treating the table of contents or a Gutenberg marker as automatically
  "the start" without showing the preview to the user (skips Gate 3).
- Jumping straight to `convert` without having shown boundary candidates or
  pronunciation flags — always show Gate 3 and Gate 6 output first.
- Reading voice/language/paths from `.env` — there is no operational config
  file; always ask in the conversation.
- Confusing a preface/dedication (`front_matter`) with the table of
  contents (`table_of_contents`) — they need different treatment; read the
  `preview` field, don't guess from `kind` alone.
- Assuming any default voice or target language instead of deriving the
  voice suggestion from the language the user actually confirmed.
- Skipping the explicit speed ask, or asking it open-ended instead of as a
  1x/1.5x/2x/3x choice, because 1x seems obvious.
- Skipping the explicit preface/intro check at Gate 3 — silently keeping or
  dropping front matter without asking, or not saying anything when none
  is detected.
- Treating decorative-line/roman-numeral cleaning (Gate 2) as something to
  ask about — it's automatic; only *report* the counts, don't gate on it.
- Showing too little surrounding text at Gate 3 to judge whether a
  preface/foreword should be included or skipped.
- Running Gate 6's `pronunciation` without `--source-lang`/`--translate-to`
  when a translation was requested — that flags risky tokens in the
  original text, not the translated text that actually gets spoken.
- Running Gate 6 (pronunciation) without first completing Gate 5
  (translate) when a translation was requested — pronunciation must
  reflect the text that will actually be spoken.
- Editing the wrong cache file at Gate 6: the flat translated cache needs no
  offset recompute, but editing the original-text cache does.

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [gomesfellipe](https://github.com/gomesfellipe)
- **Source:** [gomesfellipe/book-to-audiobook](https://github.com/gomesfellipe/book-to-audiobook)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-gomesfellipe-book-to-audiobook-book-to-audiobook
- Seller: https://agentstack.voostack.com/s/gomesfellipe
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
