95 lines
2.6 KiB
Python
95 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Add or update Table of Contents for an EPUB built by pdf_to_epub.
|
|
|
|
Since the PDF has no embedded TOC, chapter markers are defined manually
|
|
in a JSON config file. This script patches an existing EPUB's navigation.
|
|
|
|
Usage:
|
|
python3 converter/add_toc.py output/ExploringMycoFiBook.epub --toc toc_mycofi.json
|
|
|
|
TOC JSON format:
|
|
[
|
|
{"title": "Cover", "page": 1},
|
|
{"title": "Introduction", "page": 5},
|
|
{"title": "Chapter 1: Mycelial Networks", "page": 12},
|
|
...
|
|
]
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sys
|
|
import zipfile
|
|
import tempfile
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
from ebooklib import epub
|
|
|
|
|
|
def patch_toc(epub_path: str, toc_entries: list[dict]) -> str:
|
|
"""Patch the TOC of an existing EPUB with manual chapter markers.
|
|
|
|
Args:
|
|
epub_path: Path to the EPUB file
|
|
toc_entries: List of {"title": str, "page": int} dicts (1-indexed pages)
|
|
|
|
Returns:
|
|
Path to the patched EPUB
|
|
"""
|
|
book = epub.read_epub(epub_path)
|
|
|
|
# Find all page items sorted by filename
|
|
pages = sorted(
|
|
[item for item in book.get_items() if item.file_name.startswith("pages/")],
|
|
key=lambda x: x.file_name,
|
|
)
|
|
|
|
if not pages:
|
|
print("Error: No page items found in EPUB")
|
|
sys.exit(1)
|
|
|
|
# Build new TOC from entries
|
|
new_toc = []
|
|
for entry in toc_entries:
|
|
page_idx = entry["page"] - 1 # Convert 1-indexed to 0-indexed
|
|
if 0 <= page_idx < len(pages):
|
|
page_item = pages[page_idx]
|
|
# Create a link using the page's file_name
|
|
new_toc.append(epub.Link(page_item.file_name, entry["title"], f"toc_{page_idx}"))
|
|
else:
|
|
print(f"Warning: Page {entry['page']} out of range (1-{len(pages)}), skipping: {entry['title']}")
|
|
|
|
book.toc = new_toc
|
|
|
|
# Re-add navigation items
|
|
for item in list(book.get_items()):
|
|
if isinstance(item, (epub.EpubNcx, epub.EpubNav)):
|
|
book.items.remove(item)
|
|
book.add_item(epub.EpubNcx())
|
|
book.add_item(epub.EpubNav())
|
|
|
|
# Write back
|
|
epub.write_epub(epub_path, book, {})
|
|
print(f"Updated TOC with {len(new_toc)} entries in {epub_path}")
|
|
return epub_path
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Add/update EPUB table of contents")
|
|
parser.add_argument("epub", help="Path to EPUB file")
|
|
parser.add_argument("--toc", required=True, help="Path to TOC JSON file")
|
|
|
|
args = parser.parse_args()
|
|
|
|
with open(args.toc) as f:
|
|
toc_entries = json.load(f)
|
|
|
|
patch_toc(args.epub, toc_entries)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|