@@ -0,0 +1,179 @@
|
||||
"""
|
||||
Utilities for managing book library, scanning EPUBs, and extracting metadata.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Optional
|
||||
from dreader import create_ebook_reader
|
||||
import base64
|
||||
from io import BytesIO
|
||||
|
||||
|
||||
def scan_book_directory(directory: Path) -> List[Dict[str, str]]:
|
||||
"""
|
||||
Scan a directory for EPUB files and extract metadata.
|
||||
|
||||
Args:
|
||||
directory: Path to directory containing EPUB files
|
||||
|
||||
Returns:
|
||||
List of book dictionaries with metadata
|
||||
"""
|
||||
books = []
|
||||
epub_files = list(directory.glob('*.epub'))
|
||||
|
||||
for epub_path in epub_files:
|
||||
metadata = extract_book_metadata(epub_path)
|
||||
if metadata:
|
||||
books.append(metadata)
|
||||
|
||||
return sorted(books, key=lambda b: b['title'].lower())
|
||||
|
||||
|
||||
def extract_book_metadata(epub_path: Path, include_cover: bool = True) -> Optional[Dict]:
|
||||
"""
|
||||
Extract metadata from an EPUB file.
|
||||
|
||||
Args:
|
||||
epub_path: Path to EPUB file
|
||||
include_cover: Whether to extract and include cover image as base64
|
||||
|
||||
Returns:
|
||||
Dictionary with book metadata or None if extraction fails
|
||||
"""
|
||||
try:
|
||||
# Create temporary reader to extract metadata
|
||||
reader = create_ebook_reader(page_size=(400, 600))
|
||||
reader.load_epub(str(epub_path))
|
||||
|
||||
metadata = {
|
||||
'filename': epub_path.name,
|
||||
'path': str(epub_path),
|
||||
'title': reader.book_title or epub_path.stem,
|
||||
'author': reader.book_author or 'Unknown Author',
|
||||
}
|
||||
|
||||
# Extract cover image if requested
|
||||
if include_cover:
|
||||
cover_data = extract_cover_as_base64(reader)
|
||||
metadata['cover_data'] = cover_data
|
||||
|
||||
return metadata
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error extracting metadata from {epub_path}: {e}")
|
||||
return {
|
||||
'filename': epub_path.name,
|
||||
'path': str(epub_path),
|
||||
'title': epub_path.stem,
|
||||
'author': 'Unknown',
|
||||
'cover_data': None
|
||||
}
|
||||
|
||||
|
||||
def extract_cover_as_base64(reader, max_width: int = 300, max_height: int = 450) -> Optional[str]:
|
||||
"""
|
||||
Extract cover image from reader and return as base64 encoded string.
|
||||
|
||||
Args:
|
||||
reader: EbookReader instance with loaded book
|
||||
max_width: Maximum width for cover image
|
||||
max_height: Maximum height for cover image
|
||||
|
||||
Returns:
|
||||
Base64 encoded PNG image string or None
|
||||
"""
|
||||
try:
|
||||
# Get first page as cover
|
||||
cover_image = reader.get_current_page()
|
||||
|
||||
# Resize if needed
|
||||
if cover_image.width > max_width or cover_image.height > max_height:
|
||||
cover_image.thumbnail((max_width, max_height))
|
||||
|
||||
# Convert to base64
|
||||
buffer = BytesIO()
|
||||
cover_image.save(buffer, format='PNG')
|
||||
img_bytes = buffer.getvalue()
|
||||
img_base64 = base64.b64encode(img_bytes).decode('utf-8')
|
||||
|
||||
return img_base64
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error extracting cover image: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def get_chapter_list(reader) -> List[Dict]:
|
||||
"""
|
||||
Get formatted chapter list from reader.
|
||||
|
||||
Args:
|
||||
reader: EbookReader instance with loaded book
|
||||
|
||||
Returns:
|
||||
List of chapter dictionaries with index and title
|
||||
"""
|
||||
try:
|
||||
chapters = reader.get_chapters()
|
||||
result = []
|
||||
for i, chapter in enumerate(chapters):
|
||||
# Handle different chapter formats
|
||||
if isinstance(chapter, str):
|
||||
title = chapter
|
||||
elif isinstance(chapter, dict):
|
||||
title = chapter.get('title', f'Chapter {i+1}')
|
||||
elif isinstance(chapter, tuple) and len(chapter) >= 2:
|
||||
# Tuple format: (title, ...)
|
||||
title = chapter[0] if chapter[0] else f'Chapter {i+1}'
|
||||
else:
|
||||
title = f'Chapter {i+1}'
|
||||
|
||||
result.append({
|
||||
'index': i,
|
||||
'title': title
|
||||
})
|
||||
return result
|
||||
except Exception as e:
|
||||
print(f"Error getting chapters: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def get_bookmark_list(reader) -> List[Dict]:
|
||||
"""
|
||||
Get formatted bookmark list from reader.
|
||||
|
||||
Args:
|
||||
reader: EbookReader instance with loaded book
|
||||
|
||||
Returns:
|
||||
List of bookmark dictionaries
|
||||
"""
|
||||
try:
|
||||
bookmarks = reader.list_saved_positions()
|
||||
return [
|
||||
{
|
||||
'name': bookmark,
|
||||
'position': '' # Could be enhanced to show chapter/page info
|
||||
}
|
||||
for bookmark in bookmarks
|
||||
]
|
||||
except Exception as e:
|
||||
print(f"Error getting bookmarks: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def page_image_to_base64(page_image) -> str:
|
||||
"""
|
||||
Convert PIL Image to base64 encoded string.
|
||||
|
||||
Args:
|
||||
page_image: PIL Image object
|
||||
|
||||
Returns:
|
||||
Base64 encoded PNG string
|
||||
"""
|
||||
buffer = BytesIO()
|
||||
page_image.save(buffer, format='PNG')
|
||||
img_bytes = buffer.getvalue()
|
||||
return base64.b64encode(img_bytes).decode('utf-8')
|
||||
Reference in New Issue
Block a user