cd /news/ai-tools/epub-to-markdown-getting-books-into-… · home › topics › ai-tools › article
[ARTICLE · art-139339] src=dev.to ↗ pub= topic=ai-tools verified=true sentiment=↑ positive

EPUB to Markdown: Getting Books Into Obsidian, Git and Your LLM Pipeline

A developer detailed a browser-based, client-side EPUB-to-Markdown converter that parses each chapter into structured Markdown without uploading the book, and published a Python script that splits a converted book into one note per chapter for Obsidian vaults, Git repos, and LLM retrieval pipelines. The writeup notes that semantic markup such as headings, lists, tables, and links survives conversion while purely visual styling does not, and that image references are preserved as paths but image files are not exported.

by read4 min views1 publishedSep 24, 2026

Ebooks are strangely locked away. The text is right there (an EPUB is literally a ZIP of XHTML files), yet it lives inside reader apps that don't talk to the rest of your tools. You can't grep it, diff it, link it from your notes, or chunk it for a retrieval pipeline without some glue.

Markdown is the glue. Once a book is a .md file, it drops into the plain-text ecosystem: Obsidian vaults, Notion imports, static-site generators, Git repos, and any script that eats text.

This post walks through converting an EPUB to Markdown in the browser, what survives the conversion, and a couple of small scripts for the most common next steps.

You could unzip the EPUB and strip tags, or convert to TXT. That works for word-frequency experiments, but you lose the one thing that makes a book navigable: structure. Chapter titles, section headings, lists, block quotes, and links all flatten into an undifferentiated wall.

Markdown keeps that structure with almost no syntax overhead:

# headings> For humans, that means the file still reads like a book in a text editor. For machines, it means explicit hierarchy you can split on.

The iloveepub EPUB to Markdown converter does the whole thing client-side. You pick a file, each chapter is parsed and rebuilt as Markdown, a progress bar tracks it, and you download a single .md file. The book is never uploaded, which you can confirm yourself: open the Network tab in DevTools while it runs and you'll see zero upload requests. No account or API key is needed, and there's no rate limit.

Steps:

.epub..md file. That's genuinely it. There are no options to tune, which is a feature when you just want the text out.

Carried over: headings (as # levels), bold and italics, links, lists including nested ones, block quotes, code blocks, tables, and horizontal rules. The rule of thumb is that anything the book marks up semantically survives; purely visual styling (fonts, colors, custom spacing) doesn't.

Images: each one becomes a standard reference like ![alt text](original-path), preserving alt text and the original path. The image files themselves aren't exported; the output is one self-contained text file. If you need the images too, unzip the EPUB separately and copy its image folder next to the .md file. The references keep the paths used inside the book, so a quick find-and-replace on the folder prefix is usually all it takes to make them resolve in your vault.

DRM-protected books can't be opened by any browser tool, so this is for DRM-free purchases, public-domain titles, and your own documents.

Dropping a 400 KB single file into a vault works, but one note per chapter is nicer for linking and backlinks. Here's a small Python script that splits on the shallowest heading level that repeats, which is almost always the chapter level:

import re, sys, pathlib

src = pathlib.Path(sys.argv[1])
text = src.read_text(encoding="utf-8")

levels = [len(m) for m in re.findall(r"^(#{1,6}) ", text, flags=re.M)]
if not levels:
    sys.exit("no headings found")
repeated = [lv for lv in sorted(set(levels)) if levels.count(lv) > 1]
top = "#" * (repeated[0] if repeated else min(levels))

parts = re.split(rf"^(?={re.escape(top)} )", text, flags=re.M)
out = src.with_suffix("")
out.mkdir(exist_ok=True)

count = 0
for i, part in enumerate(p for p in parts if p.strip()):
    count += 1
    first = part.splitlines()[0].lstrip("# ").strip() or f"part-{i}"
    slug = re.sub(r"[^\w\- ]", "", first)[:60].strip()
    (out / f"{i:02d} {slug}.md").write_text(part, encoding="utf-8")

print(f"wrote {count} files to {out}/")

Run it with python split_md.py my-book.md, then move the folder into your vault. The zero-padded prefix keeps chapters sorted in the file explorer.

If you're feeding books to a language model for Q&A, summaries, or retrieval, Markdown is about as LLM-friendly as a book gets: one clean file, explicit heading hierarchy, and no layout noise for the model to trip over.

Headings also make natural chunk boundaries. Rather than slicing every N tokens and cutting paragraphs in half, split on headings and carry the heading path as metadata:

import re

def chunk_markdown(md: str, max_chars: int = 4000):
    path, buf, chunks = [], [], []

    def flush():
        body = "\n".join(buf).strip()
        if body:
            for i in range(0, len(body), max_chars):
                chunks.append({"section": " > ".join(path), "text": body[i:i + max_chars]})
        buf.clear()

    for line in md.splitlines():
        m = re.match(r"^(#{1,6}) (.*)", line)
        if m:
            flush()
            depth = len(m.group(1))
            path[:] = path[: depth - 1] + [m.group(2).strip()]
        else:
            buf.append(line)
    flush()
    return chunks

Each chunk arrives with its context attached ("Part Two > Chapter 7 > The Harbor"), which tends to give a retriever (and the model reading the results) far more to work with than anonymous fixed-size windows. Swap the naive character split for a token-aware splitter if you need precise budgets.

git diff them. Prose diffs are much easier to review in Markdown than in XHTML.ripgrep is a surprisingly good full-text search engine. Books deserve to be first-class text, not blobs trapped in a reader app. Converting EPUB to Markdown is the shortest path from "file on disk" to "something my tools understand." Convert one book, split it, and see how differently you use it once it lives next to your notes.

── more in #ai-tools 4 stories · sorted by recency
── more on @obsidian 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/epub-to-markdown-get…] indexed:0 read:4min 2026-09-24 · —