diff --git a/cbzhelper.py b/cbzhelper.py new file mode 100644 index 0000000..efb882a --- /dev/null +++ b/cbzhelper.py @@ -0,0 +1,163 @@ +""" +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) \ No newline at end of file