23 Commits
Author SHA1 Message Date
hyugogirubato a005ef8127 Release v3.2.0 2024-03-17 14:49:27 +01:00
hyugogirubato 0e3e3840c7 Release v3.2.0 2024-03-17 14:48:48 +01:00
hyugogirubato 4fbb7fd73a Release v3.2.0 2024-03-17 14:47:33 +01:00
hyugogirubato 2edbd2f0c7 Merge pull request #3 from domenicoblanco/main
chore: add Translator key
2024-02-18 14:04:45 +01:00
Domenico Blanco 75ceec8a44 chore: add Translator key 2024-02-13 14:12:49 +01:00
hyugogirubato af878ca167 Release v3.1.2 2023-08-13 18:27:57 +02:00
hyugogirubato ea59025499 Release v3.1.2 2023-08-13 18:26:52 +02:00
hyugogirubato da535bdc67 Merge pull request #2 from RivMt/main
Bug: Tags does not written in ComicInfo.xml (include solution)
2023-08-13 18:21:46 +02:00
San Kang c9a4d94816 Fix: Add Tags to KEYS_STRING
Helper does not write `Tags` element in `ComicInfo.xml` because there is no `Tags` in `KEYS_STRING`
2023-08-11 08:22:34 +09:00
hyugogirubato 17ceafb384 Update test 2023-06-05 12:33:00 +02:00
hyugogirubato ac00ee20a3 Update README.md 2023-06-04 16:13:47 +02:00
hyugogirubato cc974077e3 Release v3.1.1 2023-06-04 16:09:11 +02:00
hyugogirubato eebe00ee5c Update README.md 2023-06-04 16:07:41 +02:00
hyugogirubato 6bef8e8de2 Update README.md 2023-06-04 16:04:41 +02:00
hyugogirubato fe4bb165d4 Update README.md 2023-06-04 16:00:17 +02:00
hyugogirubato 8eaff56f6a Update README.md 2023-06-04 15:59:42 +02:00
hyugogirubato 1f1433a3c4 Release v3.1.0 2023-06-04 15:57:30 +02:00
hyugogirubato ffca3239cf Update README.md 2023-02-11 18:52:31 +01:00
hyugogirubato a57f1c96a8 remove ide files 2023-02-08 20:52:01 +01:00
hyugogirubato f5debc6f56 Support remote files + binary files 2023-02-08 19:51:08 +01:00
hyugogirubato 40cb3759f2 fix item_wrap 2023-02-08 19:03:45 +01:00
hyugogirubato 9ff1f4940c Add Bookmark & Key for page 2023-02-08 16:49:59 +01:00
hyugogirubato 11aada0be7 fixpage name 2023-02-07 21:13:54 +01:00
31 changed files with 785 additions and 576 deletions
+82 -7
View File
@@ -1,3 +1,7 @@
# Created by https://www.toptal.com/developers/gitignore/api/python
# Edit at https://www.toptal.com/developers/gitignore?templates=python
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
@@ -8,7 +12,6 @@ __pycache__/
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
@@ -21,9 +24,11 @@ parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
@@ -38,13 +43,17 @@ pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
@@ -53,6 +62,8 @@ coverage.xml
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
@@ -65,27 +76,61 @@ instance/
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# IPython
profile_default/
ipython_config.py
# celery beat schedule file
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# dotenv
# Environments
.env
# virtualenv
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
@@ -99,3 +144,33 @@ ENV/
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/
### Python Patch ###
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
poetry.toml
# ruff
.ruff_cache/
# LSP config files
pyrightconfig.json
# End of https://www.toptal.com/developers/gitignore/api/python
-23
View File
@@ -1,23 +0,0 @@
language: python
matrix:
include:
- python: 3.4
dist: trusty
sudo: false
- python: 3.5
dist: trusty
sudo: false
- python: 3.5-dev
dist: trusty
sudo: false
- python: 3.6
dist: trusty
sudo: false
- python: 3.6-dev
dist: trusty
sudo: false
- python: 3.7
dist: xenial
sudo: true
install:
- python setup.py -q install
+60
View File
@@ -0,0 +1,60 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.2.0] - 2024-03-17
### Changed
- New code structure.
- Deleting temporary files usage.
- Automatic correction of variable types.
- Simplifying constants.
- Binary input support for pages.
- Project `pycbzhelper` renamed to `cbz`.
## [3.1.2] - 2023-08-13
### Fixed
- Fixed missing `Tags` key, thanks to @RivMt
## [3.1.1] - 2023-06-04
### Changed
- Update `README.md`.
## [3.1.0] - 2023-06-04
### Added
- Installation with pip.
- Support for non-existent location.
- Support for web loading errors of pages.
### Fixed
- Fixed page support.
### Changed
- New name of some variables.
- Automatic cleanup of temporary files.
- New location for temporary files.
- File path now uses `pathlib`.
## [3.0.1] - 2023-02-06
### Added
- Initial release.
[3.2.0]: https://github.com/hyugogirubato/cbz/releases/tag/v3.2.0
[3.1.2]: https://github.com/hyugogirubato/cbz/releases/tag/v3.1.2
[3.1.1]: https://github.com/hyugogirubato/cbz/releases/tag/v3.1.1
[3.1.0]: https://github.com/hyugogirubato/cbz/releases/tag/v3.1.0
[3.0.1]: https://github.com/hyugogirubato/cbz/releases/tag/v3.0.1
+66 -32
View File
@@ -1,40 +1,74 @@
# pydvdfab
[![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![Release](https://img.shields.io/github/release-date/hyugogirubato/pycbzhelper?style=plastic)](https://github.com/hyugogirubato/pycbzhelper/releases)
![Total Downloads](https://img.shields.io/github/downloads/hyugogirubato/pycbzhelper/total.svg?style=plastic)
# CBZ
Python library to create a cbz file with metadata.
[![License](https://img.shields.io/github/license/hyugogirubato/cbz)](https://github.com/hyugogirubato/cbz/blob/main/LICENSE)
[![Release](https://img.shields.io/github/release-date/hyugogirubato/cbz)](https://github.com/hyugogirubato/cbz/releases)
[![Latest Version](https://img.shields.io/pypi/v/cbz)](https://pypi.org/project/cbz/)
# Usage
CBZ is a Python package designed to facilitate the creation, manipulation, and extraction of comic book archive files in the CBZ format. It allows users to programmatically generate CBZ files from a collection of images, add metadata to the archives, and unpack existing CBZ files. This library is ideal for comic book enthusiasts, archivists, and developers working on applications involving digital comic book distributions.
### Basic Usage
## Features
```python
>>> import os
>>> from pycbzhelper import Helper
>>> metadata = {
>>> "Title": "T1 - Arrête de me chauffer, Nagatoro",
>>> "Series": "Arrête de me chauffer, Nagatoro",
>>> "Number": "1",
>>> "Count": 8,
>>> "Volume": 1
>>> ...
>>> }
>>> ...
>>> helper = Helper(metadata)
>>> helper.save_cbz(
... path=os.path.join("eBooks", "Arrête de me chauffer, Nagatoro"),
... file="T1 - Arrête de me chauffer, Nagatoro",
... clear=False,
... replace=True
... )
- **Create CBZ Files**: Pack a series of images into a CBZ file with ease.
- **Extract CBZ Files**: Unpack images and metadata from existing CBZ files.
- **Manage Metadata**: Add, update, and retrieve metadata from CBZ files, including titles, authors, volume numbers, and more.
- **Support for ComicInfo.xml**: Utilizes the ComicInfo.xml standard for embedding comic book metadata within CBZ files.
- **Flexible Image Handling**: Load images from various formats and ensure they are correctly formatted and ordered within the CBZ file.
- **High-Level API**: Offers a simple, high-level API for common tasks, while also providing access to lower-level functions for advanced usage.
## Installation
To install the CBZ library, you can use pip:
```bash
pip install cbz
```
# Installation
## Quick Start
To install, you can either clone the repository and run `python setup.py install`
Here's a quick example of how to create a CBZ file from a series of images:
## About
- Graphical metadata editing software [here](https://github.com/comictagger/comictagger)
- Standard ComicInfo file structure information [here](https://github.com/Kussie/ComicInfoStandard)
- New version of ComicInfo file structure [here](https://github.com/anansi-project/comicinfo)
```python
from pathlib import Path
from cbz.page import PageInfo
from cbz.comic import ComicInfo
from cbz.constants import PageType, YesNo, Manga, AgeRating, Format
if __name__ == '__main__':
# Define the path to your images
images_path = Path('path/to/your/images')
# Load images and create page objects
pages = [PageInfo.load(path) for path in images_path.iterdir()]
# Create a ComicInfo object with your comic's metadata
comic = ComicInfo.from_pages(
pages=pages,
title='Your Comic Title',
series='Your Comic Series',
number='1',
language_iso='en',
format=Format.WEB_COMIC,
black_white=YesNo.NO,
manga=Manga.NO,
age_rating=AgeRating.PENDING
)
# Pack the comic into a CBZ file
cbz_content = comic.pack()
# Save the CBZ file
cbz_path = Path('your_comic.cbz')
cbz_path.write_bytes(cbz_content)
```
## Documentation
For detailed documentation, including the full API reference and more examples, please refer to the [official CBZ Library documentation](https://en.wikipedia.org/wiki/Comic_book_archive).
## License
The CBZ Library is released under the MIT License. See [LICENSE](LICENSE) for details.
## Acknowledgments
This library was developed with the needs of comic book fans and digital archivists in mind. We hope it helps you in managing and enjoying your comic book collections.
+5
View File
@@ -0,0 +1,5 @@
from .comic import ComicInfo
from .constants import *
from .page import PageInfo
__version__ = '3.2.0'
+110
View File
@@ -0,0 +1,110 @@
from __future__ import annotations
import io
import json
import zipfile
from typing import Union
import xmltodict
from langcodes import Language
from pathlib import Path
from cbz import utils
from cbz.constants import YesNo, Manga, AgeRating, Format
from cbz.page import PageInfo
def rating(value: int) -> int:
assert -1 <= value <= 5, 'Rating must be between 0 and 5'
return value
def language(value: str) -> str:
if value and not Language.get(value).is_valid():
raise ValueError('Invalid language')
return value
class ComicInfo:
def __init__(self, pages: [PageInfo], **kwargs):
self.__pages = pages
self.__info = {
'Title': str(kwargs.get('title', '')),
'Series': str(kwargs.get('series', '')),
'Number': str(kwargs.get('number', '')),
'Count': int(kwargs.get('count', -1)),
'Volume': int(kwargs.get('volume', -1)),
'AlternateSeries': str(kwargs.get('alternate_series', '')),
'AlternateNumber': str(kwargs.get('alternate_number', '')),
'AlternateCount': int(kwargs.get('alternate_count', -1)),
'Summary': str(kwargs.get('summary', '')),
'Notes': str(kwargs.get('notes', '')),
'Year': int(kwargs.get('year', -1)),
'Month': int(kwargs.get('month', -1)),
'Day': int(kwargs.get('day', -1)),
'Writer': str(kwargs.get('writer', '')),
'Penciller': str(kwargs.get('penciller', '')),
'Inker': str(kwargs.get('inker', '')),
'Colorist': str(kwargs.get('colorist', '')),
'Letterer': str(kwargs.get('letterer', '')),
'CoverArtist': str(kwargs.get('cover_artist', '')),
'Editor': str(kwargs.get('editor', '')),
'Translator': str(kwargs.get('translator', '')),
'Publisher': str(kwargs.get('publisher', '')),
'Imprint': str(kwargs.get('imprint', '')),
'Genre': str(kwargs.get('genre', '')),
'Tags': str(kwargs.get('tags', '')),
'Web': str(kwargs.get('web', '')),
'PageCount': len(self.__pages),
'LanguageISO': language(kwargs.get('language_iso', '')),
'Format': Format(kwargs.get('format', Format.UNKNOWN)),
'EAN': str(kwargs.get('ean', '')),
'BlackAndWhite': YesNo(kwargs.get('black_white', YesNo.UNKNOWN)),
'Manga': Manga(kwargs.get('manga', Manga.UNKNOWN)),
'Characters': str(kwargs.get('characters', '')),
'Teams': str(kwargs.get('teams', '')),
'Locations': str(kwargs.get('locations', '')),
'ScanInformation': str(kwargs.get('scan_information', '')),
'StoryArc': str(kwargs.get('story_arc', '')),
'StoryArcNumber': str(kwargs.get('story_arc_number', '')),
'SeriesGroup': str(kwargs.get('series_group', '')),
'AgeRating': AgeRating(kwargs.get('age_rating', AgeRating.UNKNOWN)),
'Pages': [{'Image': i, **page.dumps()} for i, page in enumerate(pages)],
'CommunityRating': rating(kwargs.get('community_rating', -1)),
'MainCharacterOrTeam': str(kwargs.get('main_character_or_team', '')),
'Review': str(kwargs.get('review', ''))
}
def dumps(self) -> dict:
return utils.dumps(self.__info)
def __repr__(self) -> str:
return json.dumps(self.dumps(), indent=2)
@classmethod
def from_pages(cls, pages: [PageInfo], **kwargs) -> ComicInfo:
return cls(pages, **kwargs)
@classmethod
def from_cbz(cls, path: Union[Path, str]) -> ComicInfo:
if not isinstance(path, (Path, str)):
raise ValueError(f'Expecting Path object or path string, got {path!r}')
with Path(path).open(mode='rb') as f:
return cls(cls.__unpack(f.read()))
def __unpack(self, data: bytes) -> tuple[[PageInfo], dict]:
raise NotImplemented
def pack(self) -> bytes:
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_STORED) as zip_file:
zip_file.writestr(
'ComicInfo.xml',
xmltodict.unparse({'ComicInfo': self.dumps()}, pretty=True).encode('utf-8')
)
for i, page in enumerate(self.__pages):
zip_file.writestr(f'page-{i + 1:03d}{page.suffix}', page.content)
return zip_buffer.getvalue()
+100
View File
@@ -0,0 +1,100 @@
from enum import Enum
class YesNo(Enum):
UNKNOWN = 'Unknown'
NO = 'No'
YES = 'Yes'
class Manga(Enum):
UNKNOWN = 'Unknown'
NO = 'No'
YES = 'Yes'
RIGHT_LEFT = 'YesAndRightToLeft'
class PageType(Enum):
FRONT_COVER = 'FrontCover'
INNER_COVER = 'InnerCover'
ROUNDUP = 'Roundup'
STORY = 'Story'
ADVERTISEMENT = 'Advertisement'
EDITORIAL = 'Editorial'
LETTERS = 'Letters'
PREVIEW = 'Preview'
BACK_COVER = 'BackCover'
OTHER = 'Other'
DELETED = 'Deleted'
class AgeRating(Enum):
UNKNOWN = 'Unknown'
ADULTS18 = 'Adults Only 18+'
CHILDHOOD = 'Early Childhood'
EVERYONE = 'Everyone'
EVERYONE10 = 'Everyone 10+'
G = 'G'
KIDS = 'Kids to Adults'
M = 'M'
MA15 = 'MA15+'
MATURE17 = 'Mature 17+'
PG = 'PG'
R18 = 'R18+'
PENDING = 'Rating Pending'
TEEN = 'Teen'
X18 = 'X18+'
class Format(Enum):
UNKNOWN = 'Unknown'
# ONE_SHOT = '1 Shot'
# ONE_SHOT = '1/2',
# ONE_SHOT = '1-Shot'
ANNOTATION = 'Annotation'
# ANNOTATIONS = 'Annotations'
ANNUAL = 'Annual'
ANTHOLOGY = 'Anthology'
# BLACK_WHITE = 'B&W'
# BLACK_WHITE = 'B/W'
# BLACK_WHITE = 'B&&W'
BLACK_WHITE = 'Black & White'
# BOX_SET = 'Box Set'
BOX_SET = 'Box-Set'
CROSSOVER = 'Crossover'
DIRECTOR_CUT = 'Director\'s Cut'
EPILOGUE = 'Epilogue'
EVENT = 'Event'
FCBD = 'FCBD'
FLYER = 'Flyer'
# GIANT_SIZE = 'Giant'
# GIANT_SIZE = 'Giant Size'
GIANT_SIZE = 'Giant-Size'
GRAPHIC_NOVEL = 'Graphic Novel'
# HARDCOVER = 'Hardcover'
HARDCOVER = 'Hard-Cover'
# KING_SIZE = 'King'
# KING_SIZE = 'King Size'
KING_SIZE = 'King-Size'
LIMITED_SERIES = 'Limited Series'
MAGAZINE = 'Magazine'
NSFW = 'NSFW'
# ONE_SHOT = 'One Shot'
ONE_SHOT = 'One-Shot'
POINT1 = 'Point 1'
PREVIEW = 'Preview'
PROLOGUE = 'Prologue'
REFERENCE = 'Reference'
REVIEW = 'Review'
REVIEWED = 'Reviewed'
SCANLATION = 'Scanlation'
SCRIPT = 'Script'
SERIES = 'Series'
SKETCH = 'Sketch'
SPECIAL = 'Special'
# TRADE_PAPER_BACK = 'TPB'
TRADE_PAPER_BACK = 'Trade Paper Back'
# WEB_COMIC = 'WebComic'
WEB_COMIC = 'Web Comic'
# YEAR_ONE = 'Year 1'
YEAR_ONE = 'Year One'
+53
View File
@@ -0,0 +1,53 @@
from __future__ import annotations
import base64
import json
from io import BytesIO
from typing import Union
from pathlib import Path
from PIL import Image
from cbz import utils
from cbz.constants import PageType
class PageInfo:
def __init__(self, content: bytes, **kwargs):
image = Image.open(BytesIO(content))
self.suffix = f'.{image.format.lower()}'
self.content = content
self.__info = {
# 'Image': 0,
'Type': PageType(kwargs.get('type', PageType.STORY)),
'DoublePage': bool(kwargs.get('double', False)),
'ImageSize': len(content),
'Key': str(kwargs.get('key', '')),
'Bookmark': str(kwargs.get('bookmark', '')),
'ImageWidth': int(image.width),
'ImageHeight': int(image.height)
}
def dumps(self) -> dict:
return utils.dumps(self.__info)
def __repr__(self) -> str:
return json.dumps(self.dumps(), indent=2)
@classmethod
def loads(cls, data: Union[str, bytes], **kwargs) -> PageInfo:
if isinstance(data, str):
data = base64.b64decode(data)
if not isinstance(data, bytes):
raise ValueError(f'Expecting Bytes or Base64 input, got {data!r}')
return cls(data, **kwargs)
@classmethod
def load(cls, path: Union[Path, str], **kwargs) -> PageInfo:
if not isinstance(path, (Path, str)):
raise ValueError(f'Expecting Path object or path string, got {path!r}')
with Path(path).open(mode='rb') as f:
return cls(f.read(), **kwargs)
+5
View File
@@ -0,0 +1,5 @@
from enum import Enum
def dumps(data: dict) -> dict:
return {k: v.value if isinstance(v, Enum) else v for k, v in data.items() if v and v != -1 and v != 'Unknown'}
@@ -1,75 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="ComicInfo" nillable="true" type="ComicInfo" />
<xs:element name="ComicInfo" nillable="true" type="ComicInfo"/>
<xs:complexType name="ComicInfo">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Title" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Series" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Number" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Count" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Volume" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="AlternateSeries" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="AlternateNumber" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="AlternateCount" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Summary" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Notes" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Year" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Month" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Writer" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Penciller" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Inker" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Colorist" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Letterer" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="CoverArtist" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Editor" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Publisher" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Imprint" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Genre" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Web" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="0" name="PageCount" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="LanguageISO" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Format" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="Unknown" name="BlackAndWhite" type="YesNo" />
<xs:element minOccurs="0" maxOccurs="1" default="Unknown" name="Manga" type="YesNo" />
<xs:element minOccurs="0" maxOccurs="1" name="Pages" type="ArrayOfComicPageInfo" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Title" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Series" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Number" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Count" type="xs:int"/>
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Volume" type="xs:int"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="AlternateSeries" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="AlternateNumber" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="AlternateCount" type="xs:int"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Summary" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Notes" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Year" type="xs:int"/>
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Month" type="xs:int"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Writer" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Penciller" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Inker" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Colorist" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Letterer" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="CoverArtist" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Editor" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Publisher" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Imprint" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Genre" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Web" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="0" name="PageCount" type="xs:int"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="LanguageISO" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Format" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="Unknown" name="BlackAndWhite" type="YesNo"/>
<xs:element minOccurs="0" maxOccurs="1" default="Unknown" name="Manga" type="YesNo"/>
<xs:element minOccurs="0" maxOccurs="1" name="Pages" type="ArrayOfComicPageInfo"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="YesNo">
<xs:restriction base="xs:string">
<xs:enumeration value="Unknown" />
<xs:enumeration value="No" />
<xs:enumeration value="Yes" />
<xs:enumeration value="Unknown"/>
<xs:enumeration value="No"/>
<xs:enumeration value="Yes"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="ArrayOfComicPageInfo">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" name="Page" nillable="true" type="ComicPageInfo" />
<xs:element minOccurs="0" maxOccurs="unbounded" name="Page" nillable="true" type="ComicPageInfo"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ComicPageInfo">
<xs:attribute name="Image" type="xs:int" use="required" />
<xs:attribute default="Story" name="Type" type="ComicPageType" />
<xs:attribute default="false" name="DoublePage" type="xs:boolean" />
<xs:attribute default="0" name="ImageSize" type="xs:long" />
<xs:attribute default="" name="Key" type="xs:string" />
<xs:attribute default="-1" name="ImageWidth" type="xs:int" />
<xs:attribute default="-1" name="ImageHeight" type="xs:int" />
<xs:attribute name="Image" type="xs:int" use="required"/>
<xs:attribute default="Story" name="Type" type="ComicPageType"/>
<xs:attribute default="false" name="DoublePage" type="xs:boolean"/>
<xs:attribute default="0" name="ImageSize" type="xs:long"/>
<xs:attribute default="" name="Key" type="xs:string"/>
<xs:attribute default="-1" name="ImageWidth" type="xs:int"/>
<xs:attribute default="-1" name="ImageHeight" type="xs:int"/>
</xs:complexType>
<xs:simpleType name="ComicPageType">
<xs:list>
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="FrontCover" />
<xs:enumeration value="InnerCover" />
<xs:enumeration value="Roundup" />
<xs:enumeration value="Story" />
<xs:enumeration value="Advertisement" />
<xs:enumeration value="Editorial" />
<xs:enumeration value="Letters" />
<xs:enumeration value="Preview" />
<xs:enumeration value="BackCover" />
<xs:enumeration value="Other" />
<xs:enumeration value="Deleted" />
<xs:enumeration value="FrontCover"/>
<xs:enumeration value="InnerCover"/>
<xs:enumeration value="Roundup"/>
<xs:enumeration value="Story"/>
<xs:enumeration value="Advertisement"/>
<xs:enumeration value="Editorial"/>
<xs:enumeration value="Letters"/>
<xs:enumeration value="Preview"/>
<xs:enumeration value="BackCover"/>
<xs:enumeration value="Other"/>
<xs:enumeration value="Deleted"/>
</xs:restriction>
</xs:simpleType>
</xs:list>
@@ -1,63 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="ComicInfo" nillable="true" type="ComicInfo" />
<xs:element name="ComicInfo" nillable="true" type="ComicInfo"/>
<xs:complexType name="ComicInfo">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Title" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Series" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Number" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Count" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Volume" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="AlternateSeries" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="AlternateNumber" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="AlternateCount" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Summary" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Notes" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Year" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Month" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Day" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Writer" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Penciller" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Inker" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Colorist" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Letterer" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="CoverArtist" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Editor" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Publisher" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Imprint" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Genre" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Web" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="0" name="PageCount" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="LanguageISO" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Format" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="Unknown" name="BlackAndWhite" type="YesNo" />
<xs:element minOccurs="0" maxOccurs="1" default="Unknown" name="Manga" type="Manga" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Characters" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Teams" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Locations" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="ScanInformation" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="StoryArc" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="SeriesGroup" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="Unknown" name="AgeRating" type="AgeRating" />
<xs:element minOccurs="0" maxOccurs="1" name="Pages" type="ArrayOfComicPageInfo" />
<xs:element minOccurs="0" maxOccurs="1" name="CommunityRating" type="Rating" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="MainCharacterOrTeam" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Review" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Title" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Series" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Number" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Count" type="xs:int"/>
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Volume" type="xs:int"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="AlternateSeries" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="AlternateNumber" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="AlternateCount" type="xs:int"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Summary" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Notes" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Year" type="xs:int"/>
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Month" type="xs:int"/>
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Day" type="xs:int"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Writer" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Penciller" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Inker" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Colorist" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Letterer" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="CoverArtist" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Editor" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Publisher" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Imprint" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Genre" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Web" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="0" name="PageCount" type="xs:int"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="LanguageISO" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Format" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="Unknown" name="BlackAndWhite" type="YesNo"/>
<xs:element minOccurs="0" maxOccurs="1" default="Unknown" name="Manga" type="Manga"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Characters" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Teams" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Locations" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="ScanInformation" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="StoryArc" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="SeriesGroup" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="Unknown" name="AgeRating" type="AgeRating"/>
<xs:element minOccurs="0" maxOccurs="1" name="Pages" type="ArrayOfComicPageInfo"/>
<xs:element minOccurs="0" maxOccurs="1" name="CommunityRating" type="Rating"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="MainCharacterOrTeam" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Review" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="YesNo">
<xs:restriction base="xs:string">
<xs:enumeration value="Unknown" />
<xs:enumeration value="No" />
<xs:enumeration value="Yes" />
<xs:enumeration value="Unknown"/>
<xs:enumeration value="No"/>
<xs:enumeration value="Yes"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Manga">
<xs:restriction base="xs:string">
<xs:enumeration value="Unknown" />
<xs:enumeration value="No" />
<xs:enumeration value="Yes" />
<xs:enumeration value="YesAndRightToLeft" />
<xs:enumeration value="Unknown"/>
<xs:enumeration value="No"/>
<xs:enumeration value="Yes"/>
<xs:enumeration value="YesAndRightToLeft"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Rating">
@@ -69,53 +69,53 @@
</xs:simpleType>
<xs:simpleType name="AgeRating">
<xs:restriction base="xs:string">
<xs:enumeration value="Unknown" />
<xs:enumeration value="Adults Only 18+" />
<xs:enumeration value="Early Childhood" />
<xs:enumeration value="Everyone" />
<xs:enumeration value="Everyone 10+" />
<xs:enumeration value="G" />
<xs:enumeration value="Kids to Adults" />
<xs:enumeration value="M" />
<xs:enumeration value="MA15+" />
<xs:enumeration value="Mature 17+" />
<xs:enumeration value="PG" />
<xs:enumeration value="R18+" />
<xs:enumeration value="Rating Pending" />
<xs:enumeration value="Teen" />
<xs:enumeration value="X18+" />
<xs:enumeration value="Unknown"/>
<xs:enumeration value="Adults Only 18+"/>
<xs:enumeration value="Early Childhood"/>
<xs:enumeration value="Everyone"/>
<xs:enumeration value="Everyone 10+"/>
<xs:enumeration value="G"/>
<xs:enumeration value="Kids to Adults"/>
<xs:enumeration value="M"/>
<xs:enumeration value="MA15+"/>
<xs:enumeration value="Mature 17+"/>
<xs:enumeration value="PG"/>
<xs:enumeration value="R18+"/>
<xs:enumeration value="Rating Pending"/>
<xs:enumeration value="Teen"/>
<xs:enumeration value="X18+"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="ArrayOfComicPageInfo">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" name="Page" nillable="true" type="ComicPageInfo" />
<xs:element minOccurs="0" maxOccurs="unbounded" name="Page" nillable="true" type="ComicPageInfo"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ComicPageInfo">
<xs:attribute name="Image" type="xs:int" use="required" />
<xs:attribute default="Story" name="Type" type="ComicPageType" />
<xs:attribute default="false" name="DoublePage" type="xs:boolean" />
<xs:attribute default="0" name="ImageSize" type="xs:long" />
<xs:attribute default="" name="Key" type="xs:string" />
<xs:attribute default="" name="Bookmark" type="xs:string" />
<xs:attribute default="-1" name="ImageWidth" type="xs:int" />
<xs:attribute default="-1" name="ImageHeight" type="xs:int" />
<xs:attribute name="Image" type="xs:int" use="required"/>
<xs:attribute default="Story" name="Type" type="ComicPageType"/>
<xs:attribute default="false" name="DoublePage" type="xs:boolean"/>
<xs:attribute default="0" name="ImageSize" type="xs:long"/>
<xs:attribute default="" name="Key" type="xs:string"/>
<xs:attribute default="" name="Bookmark" type="xs:string"/>
<xs:attribute default="-1" name="ImageWidth" type="xs:int"/>
<xs:attribute default="-1" name="ImageHeight" type="xs:int"/>
</xs:complexType>
<xs:simpleType name="ComicPageType">
<xs:list>
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="FrontCover" />
<xs:enumeration value="InnerCover" />
<xs:enumeration value="Roundup" />
<xs:enumeration value="Story" />
<xs:enumeration value="Advertisement" />
<xs:enumeration value="Editorial" />
<xs:enumeration value="Letters" />
<xs:enumeration value="Preview" />
<xs:enumeration value="BackCover" />
<xs:enumeration value="Other" />
<xs:enumeration value="Deleted" />
<xs:enumeration value="FrontCover"/>
<xs:enumeration value="InnerCover"/>
<xs:enumeration value="Roundup"/>
<xs:enumeration value="Story"/>
<xs:enumeration value="Advertisement"/>
<xs:enumeration value="Editorial"/>
<xs:enumeration value="Letters"/>
<xs:enumeration value="Preview"/>
<xs:enumeration value="BackCover"/>
<xs:enumeration value="Other"/>
<xs:enumeration value="Deleted"/>
</xs:restriction>
</xs:simpleType>
</xs:list>
@@ -1,66 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="ComicInfo" nillable="true" type="ComicInfo" />
<xs:element name="ComicInfo" nillable="true" type="ComicInfo"/>
<xs:complexType name="ComicInfo">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Title" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Series" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Number" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Count" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Volume" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="AlternateSeries" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="AlternateNumber" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="AlternateCount" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Summary" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Notes" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Year" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Month" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Day" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Writer" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Penciller" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Inker" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Colorist" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Letterer" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="CoverArtist" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Editor" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Translator" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Publisher" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Imprint" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Genre" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Tags" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Web" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="0" name="PageCount" type="xs:int" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="LanguageISO" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Format" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="Unknown" name="BlackAndWhite" type="YesNo" />
<xs:element minOccurs="0" maxOccurs="1" default="Unknown" name="Manga" type="Manga" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Characters" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Teams" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Locations" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="ScanInformation" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="StoryArc" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="StoryArcNumber" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="SeriesGroup" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="Unknown" name="AgeRating" type="AgeRating" />
<xs:element minOccurs="0" maxOccurs="1" name="Pages" type="ArrayOfComicPageInfo" />
<xs:element minOccurs="0" maxOccurs="1" name="CommunityRating" type="Rating" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="MainCharacterOrTeam" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Review" type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" default="" name="Title" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Series" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Number" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Count" type="xs:int"/>
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Volume" type="xs:int"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="AlternateSeries" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="AlternateNumber" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="AlternateCount" type="xs:int"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Summary" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Notes" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Year" type="xs:int"/>
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Month" type="xs:int"/>
<xs:element minOccurs="0" maxOccurs="1" default="-1" name="Day" type="xs:int"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Writer" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Penciller" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Inker" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Colorist" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Letterer" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="CoverArtist" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Editor" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Translator" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Publisher" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Imprint" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Genre" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Tags" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Web" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="0" name="PageCount" type="xs:int"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="LanguageISO" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Format" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="Unknown" name="BlackAndWhite" type="YesNo"/>
<xs:element minOccurs="0" maxOccurs="1" default="Unknown" name="Manga" type="Manga"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Characters" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Teams" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Locations" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="ScanInformation" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="StoryArc" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="StoryArcNumber" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="SeriesGroup" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="Unknown" name="AgeRating" type="AgeRating"/>
<xs:element minOccurs="0" maxOccurs="1" name="Pages" type="ArrayOfComicPageInfo"/>
<xs:element minOccurs="0" maxOccurs="1" name="CommunityRating" type="Rating"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="MainCharacterOrTeam" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" default="" name="Review" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name="YesNo">
<xs:restriction base="xs:string">
<xs:enumeration value="Unknown" />
<xs:enumeration value="No" />
<xs:enumeration value="Yes" />
<xs:enumeration value="Unknown"/>
<xs:enumeration value="No"/>
<xs:enumeration value="Yes"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Manga">
<xs:restriction base="xs:string">
<xs:enumeration value="Unknown" />
<xs:enumeration value="No" />
<xs:enumeration value="Yes" />
<xs:enumeration value="YesAndRightToLeft" />
<xs:enumeration value="Unknown"/>
<xs:enumeration value="No"/>
<xs:enumeration value="Yes"/>
<xs:enumeration value="YesAndRightToLeft"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="Rating">
@@ -72,53 +72,53 @@
</xs:simpleType>
<xs:simpleType name="AgeRating">
<xs:restriction base="xs:string">
<xs:enumeration value="Unknown" />
<xs:enumeration value="Adults Only 18+" />
<xs:enumeration value="Early Childhood" />
<xs:enumeration value="Everyone" />
<xs:enumeration value="Everyone 10+" />
<xs:enumeration value="G" />
<xs:enumeration value="Kids to Adults" />
<xs:enumeration value="M" />
<xs:enumeration value="MA15+" />
<xs:enumeration value="Mature 17+" />
<xs:enumeration value="PG" />
<xs:enumeration value="R18+" />
<xs:enumeration value="Rating Pending" />
<xs:enumeration value="Teen" />
<xs:enumeration value="X18+" />
<xs:enumeration value="Unknown"/>
<xs:enumeration value="Adults Only 18+"/>
<xs:enumeration value="Early Childhood"/>
<xs:enumeration value="Everyone"/>
<xs:enumeration value="Everyone 10+"/>
<xs:enumeration value="G"/>
<xs:enumeration value="Kids to Adults"/>
<xs:enumeration value="M"/>
<xs:enumeration value="MA15+"/>
<xs:enumeration value="Mature 17+"/>
<xs:enumeration value="PG"/>
<xs:enumeration value="R18+"/>
<xs:enumeration value="Rating Pending"/>
<xs:enumeration value="Teen"/>
<xs:enumeration value="X18+"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="ArrayOfComicPageInfo">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" name="Page" nillable="true" type="ComicPageInfo" />
<xs:element minOccurs="0" maxOccurs="unbounded" name="Page" nillable="true" type="ComicPageInfo"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ComicPageInfo">
<xs:attribute name="Image" type="xs:int" use="required" />
<xs:attribute default="Story" name="Type" type="ComicPageType" />
<xs:attribute default="false" name="DoublePage" type="xs:boolean" />
<xs:attribute default="0" name="ImageSize" type="xs:long" />
<xs:attribute default="" name="Key" type="xs:string" />
<xs:attribute default="" name="Bookmark" type="xs:string" />
<xs:attribute default="-1" name="ImageWidth" type="xs:int" />
<xs:attribute default="-1" name="ImageHeight" type="xs:int" />
<xs:attribute name="Image" type="xs:int" use="required"/>
<xs:attribute default="Story" name="Type" type="ComicPageType"/>
<xs:attribute default="false" name="DoublePage" type="xs:boolean"/>
<xs:attribute default="0" name="ImageSize" type="xs:long"/>
<xs:attribute default="" name="Key" type="xs:string"/>
<xs:attribute default="" name="Bookmark" type="xs:string"/>
<xs:attribute default="-1" name="ImageWidth" type="xs:int"/>
<xs:attribute default="-1" name="ImageHeight" type="xs:int"/>
</xs:complexType>
<xs:simpleType name="ComicPageType">
<xs:list>
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="FrontCover" />
<xs:enumeration value="InnerCover" />
<xs:enumeration value="Roundup" />
<xs:enumeration value="Story" />
<xs:enumeration value="Advertisement" />
<xs:enumeration value="Editorial" />
<xs:enumeration value="Letters" />
<xs:enumeration value="Preview" />
<xs:enumeration value="BackCover" />
<xs:enumeration value="Other" />
<xs:enumeration value="Deleted" />
<xs:enumeration value="FrontCover"/>
<xs:enumeration value="InnerCover"/>
<xs:enumeration value="Roundup"/>
<xs:enumeration value="Story"/>
<xs:enumeration value="Advertisement"/>
<xs:enumeration value="Editorial"/>
<xs:enumeration value="Letters"/>
<xs:enumeration value="Preview"/>
<xs:enumeration value="BackCover"/>
<xs:enumeration value="Other"/>
<xs:enumeration value="Deleted"/>
</xs:restriction>
</xs:simpleType>
</xs:list>
+50
View File
@@ -0,0 +1,50 @@
from pathlib import Path
from cbz.comic import ComicInfo
from cbz.constants import PageType, YesNo, Manga, AgeRating, Format
from cbz.page import PageInfo
PARENT = Path()
if __name__ == '__main__':
# @Info: Load comic pages
paths = list((PARENT / 'images').iterdir())
pages = [PageInfo.load(
path=path,
type=PageType.FRONT_COVER if i == 0 else PageType.BACK_COVER if i == len(paths) - 1 else PageType.STORY
) for i, path in enumerate(paths)]
# @Info: Load comic metadata
comic = ComicInfo.from_pages(
pages=pages,
title='T1 - Arrête de me chauffer, Nagatoro',
series='Arrête de me chauffer, Nagatoro',
number='1',
count=8,
volume=1,
summary='Nagatoro est en seconde. Pleine d\u2019assurance, joueuse, moqueuse, elle se d\u00e9couvre un jour un passe-temps favori : martyriser son \u201cSenpai\u201d, lyc\u00e9en de premi\u00e8re timide et mal dans sa peau. Nagatoro taquine, agace, aguiche, va parfois trop loin... mais qu\u2019a-t-elle vraiment derri\u00e8re la t\u00eate ? Et si derri\u00e8re ses moqueries elle cachait une v\u00e9ritable affection ? Et si finalement, ses farces permettaient \u00e0 Senpai de s\u2019affirmer ?',
year=2021,
month=3,
day=12,
writer='Nanashi',
inker='Nanashi',
editor='Noeve Grafx',
publisher='Noeve Grafx',
imprint='Noeve Grafx',
genre='Shonen',
web='http://www.izneo.com/en/manga/shonen/arrete-de-me-chauffer-nagatoro-37560/arrete-de-me-chauffer-nagatoro-86232',
language_iso='fr',
format=Format.PREVIEW,
black_white=YesNo.YES,
manga=Manga.RIGHT_LEFT,
age_rating=AgeRating.EVERYONE10,
community_rating=5,
ean='9782490676569'
)
# @Info: Pack cbz
cbz_content = comic.pack()
# @Info: Write cbz
cbz_path = PARENT / 'Arrête de me chauffer, Nagatoro.cbz'
cbz_path.write_bytes(cbz_content)

Before

Width:  |  Height:  |  Size: 247 KiB

After

Width:  |  Height:  |  Size: 247 KiB

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Before

Width:  |  Height:  |  Size: 143 KiB

After

Width:  |  Height:  |  Size: 143 KiB

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 120 KiB

Before

Width:  |  Height:  |  Size: 619 KiB

After

Width:  |  Height:  |  Size: 619 KiB

Before

Width:  |  Height:  |  Size: 592 KiB

After

Width:  |  Height:  |  Size: 592 KiB

Before

Width:  |  Height:  |  Size: 680 KiB

After

Width:  |  Height:  |  Size: 680 KiB

Before

Width:  |  Height:  |  Size: 590 KiB

After

Width:  |  Height:  |  Size: 590 KiB

Before

Width:  |  Height:  |  Size: 492 KiB

After

Width:  |  Height:  |  Size: 492 KiB

Before

Width:  |  Height:  |  Size: 479 KiB

After

Width:  |  Height:  |  Size: 479 KiB

Before

Width:  |  Height:  |  Size: 549 KiB

After

Width:  |  Height:  |  Size: 549 KiB

-4
View File
@@ -1,4 +0,0 @@
from .helper import *
from .comicinfo import *
__version__ = "3.0.1"
-44
View File
@@ -1,44 +0,0 @@
"""
Refer:
- https://github.com/comictagger/comictagger
- https://github.com/Kussie/ComicInfoStandard
- https://github.com/anansi-project/comicinfo
"""
KEYS_STRING = [
'Title', 'Series', 'Number', 'AlternateSeries', 'AlternateNumber',
'Summary', 'Notes', 'Writer', 'Penciller', 'Inker', 'Colorist',
'Letterer', 'CoverArtist', 'Editor', 'Publisher', 'Imprint', 'Genre',
'Web', 'Characters', 'Teams', 'Locations', 'ScanInformation',
'StoryArc', 'SeriesGroup', 'CommunityRating'
]
KEYS_INT = [
'Count', 'Volume', 'AlternateCount', 'Year', 'Month', 'Day', 'PageCount'
]
KEYS_SPECIAL = [
'BlackAndWhite', 'Manga', 'AgeRating', 'Pages', 'LanguageISO', 'Format', 'ESN'
]
KEYS_AGE = [
'Adults Only 18+', 'Early Childhood', 'Everyone', 'Everyone 10+',
'G', 'Kids to Adults', 'M', 'MA 15+', 'Mature 17+', 'PG', 'R18+',
'Rating Pending', 'Teen', 'X18+', 'Rating Pending'
]
KEYS_FORMAT = [
'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', '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'
]
KEYS_PAGE_TYPE = [
'FrontCover', 'InnerCover', 'Roundup', 'Story', 'Advertisment',
'Editorial', 'Letters', 'Preview', 'BackCover', 'Other', 'Deleted'
]
-14
View File
@@ -1,14 +0,0 @@
class PyCBZHelperException(Exception):
"""Exceptions used by pycbzhelper."""
class InvalidKeyValue(PyCBZHelperException):
"""The key value is invalid."""
class MissingPageFile(PyCBZHelperException):
"""No page available."""
class InvalidFilePermission(PyCBZHelperException):
"""Unable to delete existing file."""
-120
View File
@@ -1,120 +0,0 @@
import os
import shutil
import zipfile
from json2xml import json2xml
from langcodes import Language
from PIL import Image
from pycbzhelper.comicinfo import KEYS_STRING, KEYS_INT, KEYS_SPECIAL, KEYS_AGE, KEYS_FORMAT, KEYS_PAGE_TYPE
from pycbzhelper.exceptions import InvalidKeyValue, MissingPageFile, InvalidFilePermission
from pycbzhelper.utils import get_key_value, delete_none, slugify
class Helper:
def __init__(self, kwargs):
self._files = []
self.metadata = self._get_metadata(kwargs)
def _get_metadata(self, kwargs) -> str:
# NOTE: Check metadata
for key in KEYS_STRING:
if kwargs.get(key) and not isinstance(kwargs.get(key), str):
raise InvalidKeyValue(f"ERROR: Key must be string: {key}")
for key in KEYS_INT:
if kwargs.get(key) and not isinstance(kwargs.get(key), int):
raise InvalidKeyValue(f"ERROR: Key must be integer: {key}")
for key in KEYS_SPECIAL:
if kwargs.get(key):
if key == 'BlackAndWhite' and get_key_value(kwargs.get(key)) not in ['Yes', 'No']:
raise InvalidKeyValue(f"ERROR: Key must be boolean: {key}")
elif key == 'Manga' and get_key_value(kwargs.get(key)) not in ['YesAndRightToLeft', 'Yes', 'No']:
raise InvalidKeyValue(f"ERROR: Key must be boolean or special boolean: {key}")
elif key == 'AgeRating' and kwargs.get(key) not in KEYS_AGE:
raise InvalidKeyValue(f"ERROR: Key must be special age: {key}")
elif key == 'LanguageISO' and not Language.get(kwargs.get(key)).is_valid():
raise InvalidKeyValue(f"ERROR: Key must be ISO language: {key}")
elif key == 'Format' and kwargs.get(key) not in KEYS_FORMAT:
raise InvalidKeyValue(f"ERROR: Key must be special format: {key}")
elif key == 'Pages':
if isinstance(kwargs.get(key), list):
for page in kwargs.get(key):
if not page.get('File') or not isinstance(page.get('File'), str) or not os.path.exists(page.get('File')):
raise InvalidKeyValue(f"ERROR: Key must be existing string path: {key}")
if page.get('Type') and not page.get('Type') in KEYS_PAGE_TYPE:
raise InvalidKeyValue("ERROR: Key must be special type: Type")
if page.get('DoublePage') and get_key_value(page.get('DoublePage')) not in ['Yes', 'No']:
raise InvalidKeyValue("ERROR: Key must be boolean: DoublePage")
else:
raise InvalidKeyValue(f"ERROR: Key must be a list: {key}")
# NOTE: Set metadata
pages = []
if kwargs.get('Pages'):
# [{'File': 'FILE_PATH', 'Type': 'FrontCover', 'DoublePage': False}]
pages.append(" <Pages>")
kwargs['PageCount'] = len(kwargs.get('Pages'))
for i in range(kwargs.get('PageCount')):
page = kwargs.get('Pages')[i]
properties = Image.open(page['File'])
self._files.append(page['File'])
if not page.get('Type'):
page['Type'] = 'FrontCover' if i == 0 else 'Story'
item = ' <Page DoublePage="True"' if get_key_value(
page.get('DoublePage', False)) == 'Yes' else ' <Page'
pages.append(
item + ' Image="{image}" ImageHeight="{height}" ImageSize="{size}" ImageWidth="{width}" Type="{type}" />'.format(
double="False" if get_key_value(page.get('DoublePage', False)) == 'No' else "True",
image=i,
height=properties.height,
size=len(properties.fp.read()),
width=properties.width,
type=page['Type']
)
)
pages.append(" </Pages>")
del kwargs['Pages']
pages.append("</ComicInfo>")
json_data = {}
for key in KEYS_STRING + KEYS_INT + KEYS_SPECIAL:
json_data[key] = get_key_value(kwargs.get(key))
xml_data = json2xml.Json2xml(delete_none(json_data), wrapper="ComicInfo", pretty=True, attr_type=False).to_xml()
if not xml_data:
xml_data = '\n'.join(['<?xml version="1.0" ?>', '<ComicInfo>', '</ComicInfo>'])
print('WARNING: No metadata to create.')
xml_data = xml_data.replace('<?xml version="1.0" ?>', '<?xml version="1.0" encoding="utf-8"?>')
xml_data = xml_data.replace('</ComicInfo>', '\n'.join(pages))
return xml_data.strip()
def save_cbz(self, path: str, file: str, clear: bool = False, replace: bool = True) -> None:
if len(self._files) == 0:
raise MissingPageFile('ERROR: No pages available.')
output = os.path.join(path, f"{slugify(file, allow_unicode=False)}.cbz")
if not os.path.exists(path):
os.makedirs(path)
if os.path.exists(output) and not replace:
raise InvalidFilePermission('ERROR: File already exists. The replace option is not enabled.')
elif os.path.exists(output):
os.remove(output)
clear_path = []
cbz = zipfile.ZipFile(output, 'w', compression=zipfile.ZIP_STORED)
for file in self._files:
if os.path.dirname(file) not in clear_path:
clear_path.append(os.path.dirname(file))
with open(file, mode='rb') as f:
cbz.writestr(os.path.basename(file), data=f.read())
cbz.writestr('ComicInfo.xml', data=self.metadata.encode('utf-8'))
cbz.close()
print(f"INFO: File create: {output}")
if clear:
for path in clear_path:
shutil.rmtree(path)
print(f"INFO: Folder deleted: {path}")
-47
View File
@@ -1,47 +0,0 @@
from __future__ import annotations
from typing import Union
import unicodedata
def get_key_value(_value) -> str:
if isinstance(_value, str):
if _value in ['YesAndRightToLeft', 'Yes', 'No']:
return _value
elif isinstance(_value, bool):
return 'Yes' if _value else 'No'
return _value
def delete_none(_dict: dict) -> dict:
"""Delete None values recursively from all of the dictionaries, tuples, lists, sets"""
if isinstance(_dict, dict):
for key, value in list(_dict.items()):
if isinstance(value, (list, dict, tuple, set)):
_dict[key] = delete_none(value)
elif value is None or key is None:
del _dict[key]
if value == {}:
del _dict[key]
elif isinstance(_dict, (list, set, tuple)):
_dict = type(_dict)(delete_none(item) for item in _dict if item is not None)
return _dict
def slugify(value: Union[str, int], allow_unicode: bool = False) -> str:
"""
Taken from https://github.com/django/django/blob/master/django/utils/text.py
Convert to ASCII if 'allow_unicode' is False. Convert spaces or repeated
dashes to single dashes. Remove characters that aren't alphanumerics,
underscores, or hyphens. Convert to lowercase. Also strip leading and
trailing whitespace, dashes, and underscores.
"""
value = str(value)
if allow_unicode:
value = unicodedata.normalize('NFKC', value)
else:
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')
# value = re.sub(r'[^\w\s-]', '', value.lower())
# return re.sub(r'[-\s]+', '-', value).strip('-_')
return value
+33 -21
View File
@@ -1,30 +1,42 @@
"""Setup module"""
from setuptools import setup
with open('README.md', 'r') as fh:
LONG_DESCRIPTION = fh.read()
from setuptools import setup, find_packages
setup(
name='pycbzhelper',
version='3.0.1',
description='Python library to create a cbz file with metadata.',
long_description=LONG_DESCRIPTION,
long_description_content_type='text/markdown',
url='https://github.com/hyugogirubato/pycbzhelper',
name='cbz',
version='3.2.0',
author='hyugogirubato',
author_email='hyugogirubato@gmail.com',
license='GNU GPLv3',
packages=['pycbzhelper'],
install_requires=['json2xml', 'langcodes', 'pillow'],
description='CBZ is a Python package designed to facilitate the creation, manipulation, and extraction of comic book archive files in the CBZ format.',
long_description=open('README.md').read(),
long_description_content_type='text/markdown',
url='https://github.com/hyugogirubato/cbz',
packages=find_packages(),
license='GPL-3.0-only',
keywords=[
'metadata',
'manga',
'comics',
'ebooks',
'cbz',
'webtoons'
],
classifiers=[
'Environment :: Console',
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'Intended Audience :: End Users/Desktop',
'Natural Language :: English',
'Operating System :: OS Independent',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Topic :: Utilities'
]
],
install_requires=[
'requests',
'xmltodict',
'langcodes',
'Pillow'
],
python_requires='>=3.7'
)
-43
View File
@@ -1,43 +0,0 @@
import os
import pycbzhelper
if __name__ == '__main__':
pages = []
for i in range(11):
pages.append({'File': os.path.join('images', f"page-{i:03d}.jpg")})
metadata = {
'Title': 'T1 - Arrête de me chauffer, Nagatoro',
'Series': 'Arrête de me chauffer, Nagatoro',
'Number': '1',
'Count': 8,
'Volume': 1,
'Summary': 'Nagatoro est en seconde. Pleine d\u2019assurance, joueuse, moqueuse, elle se d\u00e9couvre un jour un passe-temps favori : martyriser son \u201cSenpai\u201d, lyc\u00e9en de premi\u00e8re timide et mal dans sa peau. Nagatoro taquine, agace, aguiche, va parfois trop loin... mais qu\u2019a-t-elle vraiment derri\u00e8re la t\u00eate ? Et si derri\u00e8re ses moqueries elle cachait une v\u00e9ritable affection ? Et si finalement, ses farces permettaient \u00e0 Senpai de s\u2019affirmer ?',
'Year': 2021,
'Month': 3,
'Day': 12,
'Writer': 'Nanashi',
'Inker': 'Nanashi',
'Editor': 'Noeve Grafx',
'Publisher': 'Noeve Grafx',
'Imprint': 'Noeve Grafx',
'Genre': 'Shonen',
'Web': 'http://www.izneo.com/en/manga/shonen/arrete-de-me-chauffer-nagatoro-37560/arrete-de-me-chauffer-nagatoro-86232',
'LanguageISO': 'fr',
'Format': 'Preview',
'BlackAndWhite': True,
'Manga': 'YesAndRightToLeft',
'AgeRating': 'Everyone 10+',
'CommunityRating': '5.0',
'ean': '9782490676569',
'Pages': pages
}
helper = pycbzhelper.Helper(metadata)
helper.save_cbz(
path=os.path.join('eBooks', 'Arrête de me chauffer, Nagatoro'),
file='T1 - Arrête de me chauffer, Nagatoro',
clear=False,
replace=True
)