Files
magnus919_agent-skills/epub/references/python-libraries.md
T
Magnus Hedemark 88e92f4472 feat: add epub skill v2 — 11 scripts, 9 references, 46 tests
Complete EPUB creation, editing, validation, and knowledge extraction
skill for the Agent Skills open format. Built from spec research, real
EPUB testing on 2.1MB commercial Apress title, and Apple Books
compatibility verification on macOS 26.

Scripts (11):
  epub-scaffold    — Create valid EPUB3 with cover XHTML, Apple Books CSS
  epub-edit        — Surgical editing (8 subcommands, epublib)
  epub-info        — Structure/metadata dump as JSON
  epub-text        — Clean text extraction, per-chapter or single-file
  epub-extract-knowledge — Heuristic + LLM extraction (env var auto-detect)
  epub-validate    — EPUBCheck or Python fallback validation
  epub-images      — List/extract all images with cover detection
  epub-batch       — Multi-file processing (extract-text, validate, metadata)
  epub-convert     — EPUB2→EPUB3 conversion with validation
  epub-repair      — Diagnose & auto-fix common structural issues
  epub-cover       — Add cover XHTML wrapper for Apple Books compatibility

References (9):
  epub-format-internals.md, python-libraries.md, spec-and-validation.md,
  tutorials-and-guides.md, agent-capability-discovery.md,
  fixed-layout-epub.md, accessibility.md, media-overlays.md,
  apple-books-compatibility.md (NEW — verified on macOS 26)

Test: 46/46 passing (test_epub_skill.sh)
2026-05-23 18:08:48 -04:00

11 KiB

Python Libraries for EPUB

Three main Python libraries exist for EPUB work. This reference covers their capabilities, tradeoffs, and when to use each.

Comparison

Library License EPUB Status PyPI Best For
EbookLib AGPL 2/3 Active (v0.20) ebooklib Creating & reading from scratch
epublib MIT 3 only Active (2026) epublib Editing existing EPUBs
pyepub MIT 2 only Discontinued (2020) → yael pyepub Do not use for new work

EbookLib

The most widely used Python EPUB library. Handles reading, writing, and basic manipulation of EPUB2 and EPUB3 files.

Installation

pip install EbookLib

Reading

import ebooklib
from ebooklib import epub

book = epub.read_epub('book.epub')

# Get all items of a specific type
for item in book.get_items_of_type(ebooklib.ITEM_DOCUMENT):
    print(item.get_name(), item.get_content())

# Get images
for image in book.get_items_of_type(ebooklib.ITEM_IMAGE):
    with open(image.get_name(), 'wb') as f:
        f.write(image.get_content())

# Access metadata
title = book.get_metadata('DC', 'title')
creator = book.get_metadata('DC', 'creator')

Creating

from ebooklib import epub

book = epub.EpubBook()
book.set_identifier('urn:uuid:123e4567-e89b-12d3-a456-426614174000')
book.set_title('My Book')
book.set_language('en')
book.add_author('Author Name')

# Create a chapter
c1 = epub.EpubHtml(title='Chapter 1', file_name='chap1.xhtml', lang='en')
c1.content = '<h1>Chapter 1</h1><p>Content here.</p>'
book.add_item(c1)

# Add an image
img = epub.EpubImage(
    uid='cover',
    file_name='images/cover.jpg',
    media_type='image/jpeg',
    content=open('cover.jpg', 'rb').read()
)
book.add_item(img)

# Define spine (reading order)
book.spine = ['nav', c1]

# Add navigation (EPUB3) and NCX (EPUB2 compat)
book.add_item(epub.EpubNcx())
book.add_item(epub.EpubNav())

# Write
epub.write_epub('out.epub', book)

Key Classes

Class Purpose
EpubBook The book container
EpubHtml XHTML content document (chapter)
EpubImage Image resource
EpubItem Generic resource (CSS, fonts, etc.)
EpubNcx EPUB2 NCX navigation
EpubNav EPUB3 NAV navigation
EpubCover Cover page
Link / Section TOC structure elements

Strengths

  • Most widely used — large community, many projects depend on it
  • Supports both EPUB2 and EPUB3
  • Simple, intuitive API for creating EPUBs from scratch
  • Handles cover pages, TOC, spine, and metadata well

Weaknesses

  • AGPL license — if you distribute software using EbookLib, your software must also be AGPL. Calling it at runtime (without bundling) is generally considered fine.
  • Reading large EPUBs loads everything into memory
  • Currently undergoing a refresh (see GitHub issue #318)

Documentation

epublib

A newer library (2026) designed specifically for editing existing EPUB3 files. Memory-efficient, spec-compliant, and MIT-licensed.

Installation

pip install epublib

Key Design

  • Memory-efficient: Lazy-loads resources from the ZIP as needed using Python's zipfile module. Does not load everything into memory at once.
  • Non-intrusive editing: When you open and save an EPUB, it preserves the original structure. Only modified parts are changed — the manifest isn't regenerated from scratch, metadata items keep their order, etc.
  • Spec-compliant: Follows the EPUB 3.3 specification. Resource classes mirror the spec's type hierarchy.
  • BeautifulSoup-based: Content documents (ContentDocument) expose a .soup attribute for DOM manipulation.

Editing Example

from epublib import EPUB

with EPUB('book.epub') as book:
    # Edit metadata
    book.metadata.title = 'New Title'

    # Edit all content documents
    for doc in book.documents:
        # Insert a heading at the top
        new_h1 = doc.soup.new_tag('h1', string='New Heading')
        doc.soup.body.insert(0, new_h1)

    # Add a CSS file link to all documents
    for doc in book.documents:
        new_link = doc.soup.new_tag('link', rel='stylesheet',
                                     href='../Styles/new.css',
                                     type='text/css')
        doc.soup.head.append(new_link)

    book.update_manifest_properties()
    book.write('book-modified.epub')

Resource Hierarchy

Resource
├── XMLResource (has .soup)
│   ├── PackageDocument (content.opf)
│   └── ContentDocument (XHTML, SVG)
│       └── NavigationDocument (nav.xhtml)
├── PublicationResource (has .media_type)
│   ├── ContentDocument (also above)
│   └── NCXFile (toc.ncx)

Resource Operations

# Add a resource
from epublib.resources.create import create_resource
new_resource = create_resource(xhtml_bytes, 'Text/chapter2.xhtml')
book.resources.add(resource=new_resource, add_to_spine=True, after='Text/chapter1.xhtml')

# Remove a resource
book.resources.remove('Text/chapter1.xhtml')

# Rename (auto-updates all references)
book.resources.rename('Text/chapter1.xhtml', 'Text/chapter-one.xhtml')

# Filter by media type
from epublib.media_type import MediaType, Category
pngs = book.resources.filter(MediaType.IMAGE_PNG)
images = book.resources.filter(Category.IMAGE)  # all image types

Strengths

  • MIT license — no AGPL concerns
  • Memory-efficient for large EPUBs
  • Spec-compliant resource type hierarchy
  • Non-intrusive editing preserves original structure
  • BeautifulSoup integration for content manipulation
  • Resource renaming auto-updates all references

Weaknesses

  • Newer library — smaller community, fewer tutorials
  • EPUB3 only (no EPUB2 creation, though EPUB2 files can be read)
  • Creating EPUBs from scratch requires more manual setup than EbookLib
  • Can't write to the same file (must write to temp file then copy — EOFError)

Real-World API Quirks (from testing on 2.1MB commercial EPUB)

These were discovered during integration testing and are not in the docs:

  • book.resources is iterable, not dict-like. There is no .all() method. Use for r in book.resources: directly. len(book.resources) works.
  • metadata.author doesn't exist. Dublin Core uses dc:creator. Read it via book.metadata.items — iterate and check item.name == 'creator'.
  • Write to temp file, never same file. EPUB(src) opens the ZIP, and book.write(src) will fail with EOFError because the source file is truncated while epublib still reads from it. Write to a temp file then copy: book.write(tmp); shutil.copy(tmp, src).
  • Python 3.13+ required. The wheel targets >=3.13. Install with python3 -m pip install epublib — the bare pip command may point to an older Python.
  • book.documents[0] may be SVG, not XHTML. The first content document isn't guaranteed to be the first chapter. Filter by filename or check media_type.
  • update_manifest_properties() must be called explicitly after adding or modifying resources. epublib doesn't auto-recalculate manifest properties.
  • remove_item(item) takes the item object, not a string. Use book.metadata.remove_item(item) where item is a metadata item object. The metadata.items attribute is a tuple, so .items.remove() fails.
  • First document after cover may be nav. When the cover XHTML is in the spine, book.documents ordering reflects spine order, not alphabetical.
  • book.write() with cover image >2MB is slow. The entire zip is rewritten; epublib doesn't do incremental ZIP updates. For large covers, expect 2-5 second write times.

Documentation

Common Editing Patterns (epublib)

These are the patterns used by the epub-edit CLI. When writing custom editing logic, follow these recipes:

Update metadata:

with EPUB('book.epub') as book:
    book.metadata.title = 'New Title'
    book.metadata.author = 'Author Name'
    book.metadata.language = 'fr'
    book.write('book-updated.epub')

Add a chapter from an XHTML file:

from epublib.resources.create import create_resource_from_path
with EPUB('book.epub') as book:
    new = create_resource_from_path('new-chapter.xhtml', 'Text/chapter3.xhtml')
    book.resources.add(resource=new, add_to_spine=True, add_to_toc=True,
                       after='Text/chapter2.xhtml')
    book.update_manifest_properties()
    book.write('book-expanded.epub')

Remove a chapter:

with EPUB('book.epub') as book:
    book.resources.remove('Text/chapter2.xhtml')
    book.write('book-trimmed.epub')

Reorder spine:

with EPUB('book.epub') as book:
    book.spine.reorder(['chapter3', 'chapter1', 'chapter2', 'nav'])
    book.write('book-reordered.epub')

Inject CSS into all documents:

from epublib.resources.create import create_resource_from_path
with EPUB('book.epub') as book:
    css = create_resource_from_path('dark.css', 'Styles/dark.css')
    book.resources.add(resource=css, add_to_spine=False)
    for doc in book.documents:
        link = doc.soup.new_tag('link', rel='stylesheet',
                                href='../Styles/dark.css', type='text/css')
        doc.soup.head.append(link)
    book.write('book-dark.epub')

Rename a resource (auto-updates all references):

with EPUB('book.epub') as book:
    book.resources.rename('Images/old.jpg', 'Images/new.jpg')
    book.write('book-renamed.epub')

When to Use Which

Task Use
Create a new EPUB from scratch EbookLib
Read EPUB for text extraction Either (EbookLib simpler, epublib more memory-efficient)
Edit existing EPUB (modify metadata, reorder spine, add/remove chapters) epublib
Modify content within chapters epublib (BeautifulSoup access)
Convert EPUB2 → EPUB3 epublib (non-intrusive editing)
Batch process many EPUBs epublib (memory-efficient)
Quick one-off script, don't care about license EbookLib

Running Without Installation

The scripts in this skill require at least EbookLib. To install dependencies:

pip install EbookLib
# Optional: for advanced editing
pip install epublib

License Note

EbookLib is AGPL. The scripts in this skill call it at runtime but do not bundle or distribute it. Users install it independently via pip. This is the standard pattern for MIT-licensed tools that wrap AGPL libraries.