import shutil
import zipfile
import requests
from json2xml import json2xml
from langcodes import Language
from PIL import Image
from pathlib import Path
from pycbzhelper.comicinfo import KEYS_STRING, KEYS_INT, KEYS_SPECIAL, KEYS_AGE, KEYS_FORMAT, KEYS_PAGE_TYPE
from pycbzhelper.exceptions import InvalidKeyValue, MissingPageFile, FileNotFound, InvalidFileExtension
from pycbzhelper.utils import get_key_value, delete_none
TMP = Path.home().resolve() / ".cbzhelper"
class Helper:
def __init__(self, kwargs):
self._pages = []
self.comic_info = self._comic_info(kwargs)
def _comic_info(self, kwargs) -> str:
for key in KEYS_STRING:
value = kwargs.get(key)
if value is not None and not isinstance(value, str):
raise InvalidKeyValue(f"Key must be string: {key}")
for key in KEYS_INT:
value = kwargs.get(key)
if value is not None and not isinstance(value, int):
raise InvalidKeyValue(f"Key must be integer: {key}")
for key in KEYS_SPECIAL:
value = kwargs.get(key)
if value is not None:
if key == "BlackAndWhite" and get_key_value(value) not in ["Yes", "No"]:
raise InvalidKeyValue(f"Key must be boolean: {key}")
elif key == "Manga" and get_key_value(value) not in ["YesAndRightToLeft", "Yes", "No"]:
raise InvalidKeyValue(f"Key must be boolean or special boolean: {key}")
elif key == "AgeRating" and value not in KEYS_AGE:
raise InvalidKeyValue(f"Key must be special age: {key}")
elif key == "LanguageISO" and not Language.get(value).is_valid():
raise InvalidKeyValue(f"Key must be ISO language: {key}")
elif key == "Format" and value not in KEYS_FORMAT:
raise InvalidKeyValue(f"Key must be special format: {key}")
elif key == "Pages":
if isinstance(value, list):
for file in value:
page_file = file.get("File")
if isinstance(page_file, Path):
if not page_file.is_file():
raise FileNotFound("File does not exist.")
elif isinstance(page_file, str):
if not page_file.startswith("http"):
raise InvalidKeyValue(f"Key must be an existing file path: {key}")
else:
raise InvalidKeyValue(f"Key must be an existing file path: {key}")
page_type = file.get("Type")
if page_type and page_type not in KEYS_PAGE_TYPE:
raise InvalidKeyValue("Key must be special type: Type")
page_double = file.get("DoublePage")
if page_double and get_key_value(page_double) not in ["Yes", "No"]:
raise InvalidKeyValue("Key must be boolean: DoublePage")
else:
raise InvalidKeyValue(f"Key must be a list: {key}")
xml_pages = []
pages = kwargs.get("Pages") or []
if len(pages) > 0:
# [{"File": "FILE_PATH", "Type": "FrontCover", "DoublePage": False, "Bookmark": "", "Key": ""}]
kwargs["PageCount"] = len(pages)
xml_pages.append(" ")
for i, file in enumerate(pages):
page_file = file["File"]
if isinstance(page_file, str):
r = requests.get(page_file)
r.raise_for_status()
content = r.content
else:
content = page_file.read_bytes()
page_file = TMP / f"page-{i:03d}.jpg"
page_file.parent.mkdir(parents=True, exist_ok=True)
page_file.write_bytes(content)
self._pages.append(page_file)
properties = Image.open(page_file)
page_type = file.get("Type") or "FrontCover" if i == 0 else "Story"
items = [" '.format(
double=str(page_double == "Yes"),
image=i,
height=properties.height,
size=len(properties.fp.read()),
width=properties.width,
type=page_type
))
xml_pages.append(" ")
xml_pages.append("")
kwargs.pop("Pages", None)
dict_data = {}
for key in KEYS_STRING + KEYS_INT + KEYS_SPECIAL:
dict_data[key] = get_key_value(kwargs.get(key))
xml_data = json2xml.Json2xml(delete_none(dict_data), wrapper="ComicInfo", pretty=True, attr_type=False, item_wrap=False).to_xml()
if not xml_data:
xml_data = "\n".join(['', "", ""])
print("WARNING: No metadata to create.")
xml_data = xml_data.replace('', '')
xml_data = xml_data.replace("", "\n".join(xml_pages))
return xml_data.strip()
def create_cbz(self, path: Path) -> None:
if not len(self._pages) > 0:
raise MissingPageFile("No pages available.")
if path.suffix != ".cbz":
raise InvalidFileExtension("Invalid file extension.")
path.parent.mkdir(parents=True, exist_ok=True)
cbz = zipfile.ZipFile(path, "w", compression=zipfile.ZIP_STORED)
clear_path = []
for i, page in enumerate(self._pages):
clear_path.append(page)
cbz.writestr(f"page-{i + 1:03d}{page.suffix}", data=page.read_bytes())
cbz.writestr("ComicInfo.xml", data=self.comic_info.encode("utf-8"))
cbz.close()
self._pages = []
shutil.rmtree(TMP)