This commit is contained in:
2026-04-29 01:53:16 +03:00
commit ba6bfc5ed3
14 changed files with 1338 additions and 0 deletions

127
src/exporter.py Normal file
View File

@@ -0,0 +1,127 @@
"""
Экспорт в CBZ, PDF, EPUB.
"""
import zipfile
from pathlib import Path
from typing import Literal
from loguru import logger
ExportFormat = Literal["cbz", "pdf", "epub"]
def export(
image_paths: list[Path],
output_path: Path,
fmt: ExportFormat,
title: str = "Manga",
chapter: str = "",
):
output_path.parent.mkdir(parents=True, exist_ok=True)
logger.info("Экспортирую {} страниц → {} ({})", len(image_paths), output_path.name, fmt)
if fmt == "cbz":
_export_cbz(image_paths, output_path)
elif fmt == "pdf":
_export_pdf(image_paths, output_path)
elif fmt == "epub":
_export_epub(image_paths, output_path, title, chapter)
else:
raise ValueError(f"Неизвестный формат: {fmt}")
logger.info("Сохранено: {}", output_path)
# ── CBZ ───────────────────────────────────────
def _export_cbz(images: list[Path], out: Path):
with zipfile.ZipFile(out, "w", compression=zipfile.ZIP_DEFLATED) as zf:
for i, img in enumerate(images):
zf.write(img, f"{i:04d}{img.suffix}")
# ── PDF ───────────────────────────────────────
def _export_pdf(images: list[Path], out: Path):
try:
import img2pdf
with open(out, "wb") as f:
f.write(img2pdf.convert([str(p) for p in images]))
except Exception as e:
logger.warning("img2pdf не сработал ({}), использую Pillow", e)
_export_pdf_pillow(images, out)
def _export_pdf_pillow(images: list[Path], out: Path):
from PIL import Image
pil_images = []
for p in images:
img = Image.open(p).convert("RGB")
pil_images.append(img)
if pil_images:
pil_images[0].save(
out,
save_all=True,
append_images=pil_images[1:],
format="PDF",
)
# ── EPUB ──────────────────────────────────────
def _export_epub(images: list[Path], out: Path, title: str, chapter: str):
from ebooklib import epub
from PIL import Image
import base64
book = epub.EpubBook()
book.set_identifier(f"manga-{title}-{chapter}".replace(" ", "-"))
book.set_title(f"{title}{chapter}" if chapter else title)
book.set_language("ru")
spine = ["nav"]
toc = []
for i, img_path in enumerate(images):
# Добавляем изображение в книгу
with open(img_path, "rb") as f:
img_data = f.read()
img_name = f"images/page_{i:04d}{img_path.suffix}"
epub_img = epub.EpubImage()
epub_img.file_name = img_name
epub_img.media_type = _mime(img_path.suffix)
epub_img.content = img_data
book.add_item(epub_img)
# HTML-страница для каждого изображения
page_html = epub.EpubHtml(
title=f"Страница {i + 1}",
file_name=f"page_{i:04d}.xhtml",
lang="ru",
)
page_html.content = (
f'<html><body style="margin:0;padding:0;">'
f'<img src="{img_name}" style="max-width:100%;height:auto;display:block;margin:auto;"/>'
f'</body></html>'
)
book.add_item(page_html)
spine.append(page_html)
toc.append(epub.Link(f"page_{i:04d}.xhtml", f"Страница {i + 1}", f"page{i}"))
book.toc = toc
book.spine = spine
book.add_item(epub.EpubNcx())
book.add_item(epub.EpubNav())
epub.write_epub(str(out), book)
def _mime(ext: str) -> str:
return {
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".png": "image/png",
".webp": "image/webp",
}.get(ext.lower(), "image/jpeg")