ObjDocumentEpub is a document delegate class for generating cover image icons from EPUB ebook files. It extracts cover images from EPUB files using the ebooklib library and converts them into square thumbnail icons suitable for preview purposes.
Module: factory.core/ObjDocumentEpub.py
Inherits from: ObjDocumentDelegate.ObjDocumentDelegate
Test file: resource.test/pytests/factory.core/test_ObjDocumentEpub.py
This module enables developers to:
The module uses ebooklib constants:
ebooklib.ITEM_IMAGE - Regular image itemsebooklib.ITEM_COVER - Designated cover itemsEPUB covers are typically found at:
cover.jpg or cover.png (root)images/cover.jpg (in images folder)OEBPS/images/cover.jpg (in OEBPS structure)__init__(DB: int = 0) -> NoneInitializes the ObjDocumentEpub instance.
Parameters:
DB (int, optional): Database connection identifier (default: 0)Initialization:
_IsA to "ObjDocumentEpub"extract_cover(file_path: str) -> Image.Image | NoneExtracts the cover image from an EPUB file.
Parameters:
file_path (str): Path to the EPUB fileReturns:
PIL.Image.Image: PIL Image object if cover foundNone: If no images found in EPUBProcess Flow:
epub.read_epub()Debug Logging:
"Cover image found: {filename}""Using first image as cover: {filename}"Example:
epub_handler = ObjDocumentEpub()
# Extract cover image
cover = epub_handler.extract_cover("/ebooks/mybook.epub")
if cover:
# Save cover
cover.save("/covers/mybook_cover.png")
print(f"Cover size: {cover.size}")
else:
print("No cover found")
Advanced Usage:
epub_handler = ObjDocumentEpub()
# Extract and analyze cover
cover = epub_handler.extract_cover("/ebooks/book.epub")
if cover:
width, height = cover.size
print(f"Cover dimensions: {width}x{height}")
print(f"Cover format: {cover.format}")
print(f"Cover mode: {cover.mode}")
# Resize for different uses
thumbnail = cover.resize((200, 300))
icon = cover.resize((64, 64))
_do_generate_icon(input_path: str, output_path: str, icon_size: int) -> NoneInternal method that extracts EPUB cover and generates a square thumbnail icon.
Parameters:
input_path (str): Path to the input EPUB fileoutput_path (str): Path where the icon will be savedicon_size (int): Desired size of the square icon in pixelsProcess Flow:
extract_cover() to get cover imageDocumentTools.resize_to_square() to resizeExample:
epub_handler = ObjDocumentEpub()
# Generate 128x128 thumbnail
epub_handler._do_generate_icon(
"/ebooks/novel.epub",
"/thumbnails/novel_thumb.png",
128
)
# Generate 256x256 thumbnail
epub_handler._do_generate_icon(
"/ebooks/textbook.epub",
"/thumbnails/textbook_thumb.png",
256
)
from ObjDocumentEpub import ObjDocumentEpub
# Create instance
epub = ObjDocumentEpub()
# Extract cover
cover = epub.extract_cover("/ebooks/mybook.epub")
if cover:
cover.save("/covers/mybook_cover.png")
print("Cover extracted successfully")
else:
print("No cover found in EPUB")
from ObjDocumentEpub import ObjDocumentEpub
epub = ObjDocumentEpub()
# Generate 200x200 thumbnail
epub._do_generate_icon(
"/ebooks/cookbook.epub",
"/thumbnails/cookbook.png",
200
)
from ObjDocumentEpub import ObjDocumentEpub
import os
epub = ObjDocumentEpub()
ebooks_dir = "/ebooks"
output_dir = "/thumbnails"
for filename in os.listdir(ebooks_dir):
if filename.lower().endswith(".epub"):
input_path = os.path.join(ebooks_dir, filename)
output_name = os.path.splitext(filename)[0] + "_thumb.png"
output_path = os.path.join(output_dir, output_name)
try:
epub._do_generate_icon(input_path, output_path, 128)
print(f"Generated thumbnail for {filename}")
except Exception as e:
print(f"Failed for {filename}: {e}")
from ObjDocumentEpub import ObjDocumentEpub
epub = ObjDocumentEpub()
# Extract and analyze cover
cover = epub.extract_cover("/ebooks/book.epub")
if cover:
print(f"Format: {cover.format}")
print(f"Size: {cover.size}")
print(f"Mode: {cover.mode}")
# Check aspect ratio
width, height = cover.size
ratio = width / height
if ratio > 0.7:
print("Cover is landscape or square")
else:
print("Cover is portrait (typical for books)")
from ObjDocumentEpub import ObjDocumentEpub
import ebooklib
from ebooklib import epub as epub_lib
class EbookLibraryManager:
def __init__(self):
self.epub_handler = ObjDocumentEpub()
def get_metadata(self, epub_path):
"""Extract EPUB metadata."""
book = epub_lib.read_epub(epub_path)
return {
"title": book.get_metadata("DC", "title")[0][0]
if book.get_metadata("DC", "title") else "Unknown",
"author": book.get_metadata("DC", "creator")[0][0]
if book.get_metadata("DC", "creator") else "Unknown",
"language": book.get_metadata("DC", "language")[0][0]
if book.get_metadata("DC", "language") else "Unknown",
}
def process_ebook(self, epub_path, output_dir):
"""Process EPUB: extract metadata and generate thumbnail."""
# Get metadata
metadata = self.get_metadata(epub_path)
# Generate thumbnail
filename = os.path.basename(epub_path)
thumb_path = os.path.join(
output_dir,
f"{os.path.splitext(filename)[0]}_thumb.png"
)
self.epub_handler._do_generate_icon(epub_path, thumb_path, 150)
return {
"path": epub_path,
"thumbnail": thumb_path,
"metadata": metadata
}
The module has comprehensive test coverage including:
| Test Case | Description | Status |
|---|---|---|
test_isa_set |
Validates _IsA attribute initialization | ✓ |
test_finds_cover_by_name |
Tests cover detection by filename | ✓ |
test_falls_back_to_first_image |
Tests fallback to first image | ✓ |
test_returns_none_when_no_images |
Tests handling of EPUBs without images | ✓ |
test_extracts_and_saves_cover |
Tests icon generation pipeline | ✓ |
test_no_cover_logs_message |
Tests logging when no cover found | ✓ |
test_handles_exception |
Tests error handling | ✓ |
Test file location: resource.test/pytests/factory.core/test_ObjDocumentEpub.py
Run tests:
pytest resource.test/pytests/factory.core/test_ObjDocumentEpub.py -v
ebooklib - EPUB file reading and parsingPIL (Pillow) - Image processingio - Byte stream handling (Python standard library)ObjDocumentDelegate - Base delegate classObjDocumentTools.DocumentTools - Thumbnail resizing utilitiesInstallation:
pip install ebooklib Pillow
EPUB is essentially a ZIP archive containing:
The module searches in this order:
item.get_type() in {ITEM_IMAGE, ITEM_COVER}Common cover filenames:
cover.jpg, cover.pngCover.jpg, COVER.PNGimages/cover.jpgOEBPS/Images/cover.pngfrom ObjDocumentEpub import ObjDocumentEpub
import os
def create_library_thumbnails(library_dir, thumb_dir, size=150):
"""Generate thumbnails for all EPUBs in library."""
epub = ObjDocumentEpub()
results = {"success": 0, "failed": 0, "no_cover": 0}
for filename in os.listdir(library_dir):
if not filename.lower().endswith(".epub"):
continue
epub_path = os.path.join(library_dir, filename)
thumb_path = os.path.join(
thumb_dir,
f"{os.path.splitext(filename)[0]}.png"
)
try:
cover = epub.extract_cover(epub_path)
if cover:
cover.save(thumb_path, "PNG")
from ObjDocumentTools import DocumentTools
DocumentTools.resize_to_square(thumb_path, size)
results["success"] += 1
else:
results["no_cover"] += 1
except Exception as e:
print(f"Error processing {filename}: {e}")
results["failed"] += 1
return results
from ObjDocumentEpub import ObjDocumentEpub
def check_cover_quality(epub_path):
"""Check if EPUB has a good quality cover."""
epub = ObjDocumentEpub()
cover = epub.extract_cover(epub_path)
if not cover:
return {"has_cover": False}
width, height = cover.size
megapixels = (width * height) / 1_000_000
return {
"has_cover": True,
"width": width,
"height": height,
"megapixels": round(megapixels, 2),
"aspect_ratio": round(width / height, 2),
"is_high_quality": width >= 1200 and height >= 1600,
"is_portrait": height > width
}
# Usage
quality = check_cover_quality("/ebooks/novel.epub")
print(f"Cover quality: {quality}")
from ObjDocumentEpub import ObjDocumentEpub
from ObjDocumentTools import DocumentTools
def generate_multiple_thumbnails(epub_path, output_dir):
"""Generate thumbnails in multiple sizes."""
epub = ObjDocumentEpub()
cover = epub.extract_cover(epub_path)
if not cover:
return None
sizes = {
"small": 64,
"medium": 128,
"large": 256,
"xlarge": 512
}
base_name = os.path.splitext(os.path.basename(epub_path))[0]
thumbnails = {}
for size_name, size_px in sizes.items():
output_path = os.path.join(
output_dir,
f"{base_name}_{size_name}.png"
)
cover.save(output_path, "PNG")
DocumentTools.resize_to_square(output_path, size_px)
thumbnails[size_name] = output_path
return thumbnails
Error: ModuleNotFoundError: No module named 'ebooklib'
Solution:
pip install ebooklib
Error: zipfile.BadZipFile: File is not a zip file
Cause: EPUB file is corrupted or not a valid EPUB
Solution:
# Verify EPUB structure
unzip -t book.epub
# Or use epubcheck tool
java -jar epubcheck.jar book.epub
Issue: extract_cover() returns None for valid EPUB
Possible Causes:
Solution:
# Manually inspect EPUB structure
import zipfile
with zipfile.ZipFile("book.epub", "r") as z:
print(z.namelist()) # List all files
# Check for images in unusual locations
import ebooklib
from ebooklib import epub
book = epub.read_epub("book.epub")
for item in book.get_items():
print(f"{item.get_name()} - Type: {item.get_type()}")
Issue: MemoryError when processing EPUB with very large cover
Solution:
epub = ObjDocumentEpub()
cover = epub.extract_cover("/ebooks/large_cover.epub")
if cover:
# Resize immediately to reduce memory
cover.thumbnail((1000, 1000)) # Max dimension 1000px
cover.save("/output/cover.png")
Error: Cannot identify image file from EPUB
Solution:
# Handle various image formats
from PIL import Image
import io
epub = ObjDocumentEpub()
cover = epub.extract_cover("/ebooks/book.epub")
if cover:
# Convert to RGB if needed
if cover.mode in ("RGBA", "P"):
cover = cover.convert("RGB")
cover.save("/output/cover.jpg", "JPEG")
ObjDocumentDelegate.py - Base class for document delegatesObjDocumentTools.py - Shared thumbnail utilitiesObjDocumentVideo.py - Video file thumbnail generationObjDocument.py - Main document handler that delegates to format-specific handlersextract_cover() will re-read the EPUB file