novela/containers/novela/cbr.py

62 lines
1.8 KiB
Python

from io import BytesIO
from pathlib import Path
import zipfile
import rarfile
from PIL import Image, ImageOps
SUPPORTED_IMG = {".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp"}
def _is_cbz(path: Path) -> bool:
return path.suffix.lower() == ".cbz"
def cbr_page_list(path: Path) -> list[str]:
if _is_cbz(path):
with zipfile.ZipFile(path) as zf:
names = [n for n in zf.namelist() if Path(n).suffix.lower() in SUPPORTED_IMG]
else:
with rarfile.RarFile(path) as rf:
names = [n for n in rf.namelist() if Path(n).suffix.lower() in SUPPORTED_IMG]
return sorted(names)
def cbr_page_count(path: Path) -> int:
return len(cbr_page_list(path))
def cbr_get_page(path: Path, page_num: int) -> tuple[bytes, str]:
pages = cbr_page_list(path)
if page_num < 0 or page_num >= len(pages):
raise IndexError("Page out of range")
name = pages[page_num]
ext = Path(name).suffix.lower().lstrip(".")
mime = {
"jpg": "image/jpeg",
"jpeg": "image/jpeg",
"png": "image/png",
"webp": "image/webp",
"gif": "image/gif",
"bmp": "image/bmp",
}.get(ext, "image/jpeg")
if _is_cbz(path):
with zipfile.ZipFile(path) as zf:
return zf.read(name), mime
with rarfile.RarFile(path) as rf:
return rf.read(name), mime
def cbr_cover_thumb(path: Path) -> bytes:
data, _ = cbr_get_page(path, 0)
with Image.open(BytesIO(data)) as im:
im = ImageOps.exif_transpose(im)
if im.mode not in ("RGB", "RGBA"):
im = im.convert("RGB")
thumb = ImageOps.fit(im, (300, 450), method=Image.Resampling.LANCZOS)
out = BytesIO()
thumb.save(out, format="WEBP", quality=82, method=6)
return out.getvalue()