""" Project: cbzhelper File: cbzhelper.py Author: hyuogirubato Date: 2022.08.30 """ import os.path import re import shutil import zipfile from PIL import Image def clear(value): return re.sub('[^\w\-_\.\(\)\[\] ]', '_', value) class Helper: def __init__(self, path, file, replace=True): self.path = path self.file = f"{clear(file)}.cbz" self.output = os.path.join(self.path, self.file) self.tmp = os.path.join(self.path, 'tmp') self.pages = [] if not os.path.exists(path): os.makedirs(path) if os.path.exists(self.output) and replace: os.remove(self.output) # https://github.com/Kussie/ComicInfoStandard/blob/main/ComicInfo.xsd def addMetadata(self, metadata): metadata['Pages'] = self.pages comic_config = { 'str': ['Title', 'Series', 'Number', 'AlternateSeries', 'AlternateNumber', 'Summary', 'Notes', 'Writer', 'Penciller', 'Inker', 'Colorist', 'Letterer', 'CoverArtist', 'Editor', 'Publisher', 'Imprint', 'Genre', 'Web', 'LanguageISO', 'Characters', 'Teams', 'Locations', 'ScanInformation', 'StoryArc', 'SeriesGroup'], 'int': ['Count', 'Volume', 'AlternateCount', 'Year', 'Month', 'PageCount'], 'float': ['CommunityRating', 'BookPrice'], 'YesNo': ['BlackAndWhite'], 'Manga': ['Manga'], 'AgeRating': ['AgeRating'], 'ArrayOfComicPageInfo': ['Pages'], 'Format': ['Format'] } page_config = { 'int': ['Image', 'ImageWidth', 'ImageHeight'], 'bool': ['DoublePage'], 'long': ['ImageSize'], 'str': ['Key'], 'ComicPageType': ['Type'] } comic_info = [] comic_info.append("") comic_info.append('') metadata_keys = metadata.keys() # default if not 'BlackAndWhite' in metadata_keys: metadata['BlackAndWhite'] = 'Unknown' if not 'Manga' in metadata_keys: metadata['Manga'] = 'Unknown' if not 'AgeRating' in metadata_keys: metadata['AgeRating'] = 'Unknown' for key in metadata_keys: if key in comic_config['YesNo'] and metadata[key] in ['No', 'Yes']: comic_info.append(f' <{key}>{metadata[key]}') elif key in comic_config['Manga'] and metadata[key] in ['No', 'Yes', 'YesAndRightToLeft']: comic_info.append(f' <{key}>{metadata[key]}') elif key in comic_config['AgeRating'] and metadata[key] in ['Everyone', 'G', 'Early Childhood', 'Everyone 10+', 'PG', 'Kids to Adults', 'Teen', 'M', 'MA15+', 'Mature 17+', 'R18+', 'X18+', 'Adults Only 18+', 'Rating Pending']: comic_info.append(f' <{key}>{metadata[key]}') elif key in comic_config['str']: comic_info.append(f' <{key}>{metadata[key]}') elif key in comic_config['int']: comic_info.append(f' <{key}>{metadata[key]}') elif key in comic_config['float']: comic_info.append(f' <{key}>{metadata[key]}') elif key in comic_config['Format'] and metadata[key] in ['.1', '-1', '1 Shot', '1/2', '1-Shot', 'Annotation', 'Annotations', 'Annual', 'Anthology', 'B&W', 'B/W', 'B&&W', 'Black & White', 'Box Set', 'Box-Set', 'Crossover', 'Director\'s Cut', 'Epilogue', 'Event', 'FCBD', 'Flyer', 'Giant', 'Giant Size', 'Graphic Novel', 'Hardcover', 'Hard-Cover', 'King', 'King Size', 'King-Size', 'Limited Series', 'Magazine', 'NSFW', 'One Shot', 'One-Shot', 'Point 1', 'Preview', 'Prologue', 'Reference', 'Review', 'Reviewed', 'Scanlation', 'Script', 'Series', 'Sketch', 'Special', 'TPB', 'Trade Paper Back', 'WebComic', 'Web Comic', 'Year 1', 'Year One']: comic_info.append(f' <{key}>{metadata[key]}') elif key in comic_config['ArrayOfComicPageInfo']: comic_info.append(' ') for item in metadata[key]: item_keys =item.keys() line = [] line.append(' ') comic_info.append(' '.join(line)) comic_info.append(' ') comic_info.append('') with open(os.path.join(self.tmp, 'ComicInfo.xml'), mode='w', encoding='utf-8') as f: f.write('\n'.join(comic_info)) def addPage(self, image, content, double_page=False, page_type='DEFAULT', extension='jpg'): page = os.path.join(self.tmp, f"page-{image:03d}.{extension}") if not os.path.exists(page): with open(page, mode='wb') as f: f.write(content) properties = Image.open(page) if page_type == 'DEFAULT': page_type = 'FrontCover' if image == 1 else 'Story' self.pages.append({ 'Image': image, 'Type': page_type, 'DoublePage': double_page, 'ImageSize': properties.size, 'ImageWidth': properties.width, 'ImageHeight': properties.height }) def saveCBZ(self): if os.path.exists(self.tmp): if not os.path.exists(self.output): cbz = zipfile.ZipFile(f"{self.output}.cbz", 'w', compression=zipfile.ZIP_STORED) for file in sorted(os.listdir(self.tmp), key=len): with open(os.path.join(self.tmp, file), mode='rb') as f: cbz.writestr(file, data=f.read()) cbz.close() shutil.rmtree(self.tmp)