mirror of
https://github.com/hyugogirubato/cbz.git
synced 2026-09-26 13:31:00 +00:00
+8
-104
@@ -3,123 +3,26 @@ from __future__ import annotations
|
|||||||
import json
|
import json
|
||||||
import zipfile
|
import zipfile
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
from pathlib import Path
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import xmltodict
|
import xmltodict
|
||||||
from langcodes import Language
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from cbz import utils
|
from cbz import utils
|
||||||
from cbz.constants import YesNo, Manga, AgeRating, Format, xml_name
|
from cbz.constants import xml_name
|
||||||
from cbz.models import ComicModel
|
from cbz.models import ComicModel
|
||||||
from cbz.page import PageInfo
|
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(ComicModel):
|
class ComicInfo(ComicModel):
|
||||||
def __init__(self, pages: [PageInfo], **kwargs):
|
def __init__(self, pages: [PageInfo], **kwargs):
|
||||||
self.__pages = pages
|
self.__pages = pages
|
||||||
# TODO: need change to loop with setattr
|
super(ComicInfo, self).__init__(kwargs)
|
||||||
self.title = str(kwargs.get('title', kwargs.get('Title', '')))
|
|
||||||
self.series = str(kwargs.get('series', kwargs.get('Series', '')))
|
|
||||||
self.number = str(kwargs.get('number', kwargs.get('Number', '')))
|
|
||||||
self.count = int(kwargs.get('count', kwargs.get('Count', -1)))
|
|
||||||
self.volume = int(kwargs.get('volume', kwargs.get('Volume', -1)))
|
|
||||||
self.alternate_series = str(kwargs.get('alternate_series', kwargs.get('AlternateSeries', '')))
|
|
||||||
self.alternate_number = str(kwargs.get('alternate_number', kwargs.get('AlternateNumber', '')))
|
|
||||||
self.alternate_count = int(kwargs.get('alternate_count', kwargs.get('AlternateCount', -1)))
|
|
||||||
self.summary = str(kwargs.get('summary', kwargs.get('Summary', '')))
|
|
||||||
self.notes = str(kwargs.get('notes', kwargs.get('Notes', '')))
|
|
||||||
self.year = int(kwargs.get('year', kwargs.get('Year', -1)))
|
|
||||||
self.month = int(kwargs.get('month', kwargs.get('Month', -1)))
|
|
||||||
self.day = int(kwargs.get('day', kwargs.get('Day', -1)))
|
|
||||||
self.writer = str(kwargs.get('writer', kwargs.get('Writer', '')))
|
|
||||||
self.penciller = str(kwargs.get('penciller', kwargs.get('Penciller', '')))
|
|
||||||
self.inker = str(kwargs.get('inker', kwargs.get('Inker', '')))
|
|
||||||
self.colorist = str(kwargs.get('colorist', kwargs.get('Colorist', '')))
|
|
||||||
self.letterer = str(kwargs.get('letterer', kwargs.get('Letterer', '')))
|
|
||||||
self.cover_artist = str(kwargs.get('cover_artist', kwargs.get('CoverArtist', '')))
|
|
||||||
self.editor = str(kwargs.get('editor', kwargs.get('Editor', '')))
|
|
||||||
self.translator = str(kwargs.get('translator', kwargs.get('Translator', '')))
|
|
||||||
self.publisher = str(kwargs.get('publisher', kwargs.get('Publisher', '')))
|
|
||||||
self.imprint = str(kwargs.get('imprint', kwargs.get('imprint', 'Imprint')))
|
|
||||||
self.genre = str(kwargs.get('genre', kwargs.get('Genre', '')))
|
|
||||||
self.tags = str(kwargs.get('tags', kwargs.get('Tags', '')))
|
|
||||||
self.web = str(kwargs.get('web', kwargs.get('Web', '')))
|
|
||||||
self.language_iso = language(kwargs.get('language_iso', kwargs.get('LanguageISO', '')))
|
|
||||||
self.format = Format(kwargs.get('format', kwargs.get('Format', Format.UNKNOWN)))
|
|
||||||
self.ean = str(kwargs.get('ean', kwargs.get('EAN', '')))
|
|
||||||
self.black_white = YesNo(kwargs.get('black_white', kwargs.get('BlackAndWhite', YesNo.UNKNOWN)))
|
|
||||||
self.manga = Manga(kwargs.get('manga', kwargs.get('Manga', Manga.UNKNOWN)))
|
|
||||||
self.characters = str(kwargs.get('characters', kwargs.get('Characters', '')))
|
|
||||||
self.teams = str(kwargs.get('teams', kwargs.get('Teams', '')))
|
|
||||||
self.locations = str(kwargs.get('locations', kwargs.get('Locations', '')))
|
|
||||||
self.scan_information = str(kwargs.get('scan_information', kwargs.get('ScanInformation', '')))
|
|
||||||
self.story_arc = str(kwargs.get('story_arc', kwargs.get('StoryArc', '')))
|
|
||||||
self.story_arc_number = str(kwargs.get('story_arc_number', kwargs.get('StoryArcNumber', '')))
|
|
||||||
self.series_group = str(kwargs.get('series_group', kwargs.get('series_group', '')))
|
|
||||||
self.age_rating = AgeRating(kwargs.get('age_rating', kwargs.get('AgeRating', AgeRating.UNKNOWN)))
|
|
||||||
self.community_rating = rating(kwargs.get('community_rating', kwargs.get('CommunityRating', -1)))
|
|
||||||
self.main_character_or_team = str(kwargs.get('main_character_or_team', kwargs.get('MainCharacterOrTeam', '')))
|
|
||||||
self.review = str(kwargs.get('review', kwargs.get('Review', '')))
|
|
||||||
|
|
||||||
def dumps(self) -> dict:
|
def dumps(self) -> dict:
|
||||||
# TODO: need change to loop with getattr
|
return utils.dumps(self._get() | {
|
||||||
return utils.dumps({
|
"Pages": [{'Image': i, **page.dumps()} for i, page in enumerate(self.__pages)],
|
||||||
'Title': str(self.title),
|
'PageCount': len(self.__pages)
|
||||||
'Series': str(self.series),
|
|
||||||
'Number': str(self.number),
|
|
||||||
'Count': int(self.count),
|
|
||||||
'Volume': int(self.volume),
|
|
||||||
'AlternateSeries': str(self.alternate_series),
|
|
||||||
'AlternateNumber': str(self.alternate_number),
|
|
||||||
'AlternateCount': int(self.alternate_count),
|
|
||||||
'Summary': str(self.summary),
|
|
||||||
'Notes': str(self.notes),
|
|
||||||
'Year': int(self.year),
|
|
||||||
'Month': int(self.month),
|
|
||||||
'Day': int(self.day),
|
|
||||||
'Writer': str(self.writer),
|
|
||||||
'Penciller': str(self.penciller),
|
|
||||||
'Inker': str(self.inker),
|
|
||||||
'Colorist': str(self.colorist),
|
|
||||||
'Letterer': str(self.letterer),
|
|
||||||
'CoverArtist': str(self.cover_artist),
|
|
||||||
'Editor': str(self.editor),
|
|
||||||
'Translator': str(self.translator),
|
|
||||||
'Publisher': str(self.publisher),
|
|
||||||
'Imprint': str(self.imprint),
|
|
||||||
'Genre': str(self.genre),
|
|
||||||
'Tags': str(self.tags),
|
|
||||||
'Web': str(self.web),
|
|
||||||
'PageCount': len(self.__pages),
|
|
||||||
'LanguageISO': language(self.language_iso),
|
|
||||||
'Format': self.format,
|
|
||||||
'EAN': str(self.ean),
|
|
||||||
'BlackAndWhite': self.black_white,
|
|
||||||
'Manga': self.manga,
|
|
||||||
'Characters': str(self.characters),
|
|
||||||
'Teams': str(self.teams),
|
|
||||||
'Locations': str(self.locations),
|
|
||||||
'ScanInformation': str(self.scan_information),
|
|
||||||
'StoryArc': str(self.story_arc),
|
|
||||||
'StoryArcNumber': str(self.story_arc_number),
|
|
||||||
'SeriesGroup': str(self.series_group),
|
|
||||||
'AgeRating': self.age_rating,
|
|
||||||
'Pages': [{'Image': i, **page.dumps()} for i, page in enumerate(self.__pages)],
|
|
||||||
'CommunityRating': rating(self.community_rating),
|
|
||||||
'MainCharacterOrTeam': str(self.main_character_or_team),
|
|
||||||
'Review': str(self.review)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
@@ -166,8 +69,9 @@ class ComicInfo(ComicModel):
|
|||||||
if not isinstance(path, (Path, str)):
|
if not isinstance(path, (Path, str)):
|
||||||
raise ValueError(f'Expecting Path object or path string, got {path!r}')
|
raise ValueError(f'Expecting Path object or path string, got {path!r}')
|
||||||
self._filepath = path
|
self._filepath = path
|
||||||
|
new_file = self.pack()
|
||||||
with Path(path).open(mode='wb') as f:
|
with Path(path).open(mode='wb') as f:
|
||||||
f.write(self.pack())
|
f.write(new_file)
|
||||||
|
|
||||||
def save(self) -> None:
|
def save(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
from langcodes import Language
|
||||||
|
|
||||||
xml_name = "ComicInfo.xml"
|
xml_name = "ComicInfo.xml"
|
||||||
|
|
||||||
@@ -100,3 +101,64 @@ class Format(Enum):
|
|||||||
WEB_COMIC = 'Web Comic'
|
WEB_COMIC = 'Web Comic'
|
||||||
# YEAR_ONE = 'Year 1'
|
# YEAR_ONE = 'Year 1'
|
||||||
YEAR_ONE = 'Year One'
|
YEAR_ONE = 'Year One'
|
||||||
|
|
||||||
|
|
||||||
|
class ValidRating(float):
|
||||||
|
def __init__(self, val):
|
||||||
|
assert -1 <= float(val) <= 5, f'Rating must be between 0 and 5, input {val}'
|
||||||
|
super(ValidRating, self).__init__()
|
||||||
|
|
||||||
|
|
||||||
|
class ValidLanguage(str):
|
||||||
|
def __init__(self, val):
|
||||||
|
if val and not Language.get(str(val)).is_valid():
|
||||||
|
raise ValueError(f'Invalid {val} language')
|
||||||
|
super(ValidLanguage, self).__init__()
|
||||||
|
|
||||||
|
|
||||||
|
FIELDS = (
|
||||||
|
# model: (key, xml key): (variable name, (expected type, second expected type,...))
|
||||||
|
# in case of multiple expected formats, value cast to first type in tuple
|
||||||
|
(("title", "Title"), ("title", (str,))),
|
||||||
|
(("series", "Series"), ("series", (str,))),
|
||||||
|
(("number", "Number"), ("number", (str, int, float))),
|
||||||
|
(("count", "Count"), ("count", (int,))),
|
||||||
|
(("volume", "Volume"), ("volume", (int,))),
|
||||||
|
(("alternate_series", "AlternateSeries"), ("alternate_series", (str,))),
|
||||||
|
(("alternate_number", "AlternateNumber"), ("alternate_number", (str, int, float))),
|
||||||
|
(("alternate_count", "AlternateCount"), ("alternate_count", (int,))),
|
||||||
|
(("summary", "Summary"), ("summary", (str,))),
|
||||||
|
(("notes", "Notes"), ("notes", (str,))),
|
||||||
|
(("year", "Year"), ("year", (int,))),
|
||||||
|
(("month", "Month"), ("month", (int,))),
|
||||||
|
(("day", "Day"), ("day", (int,))),
|
||||||
|
(("writer", "Writer"), ("writer", (str,))),
|
||||||
|
(("penciller", "Penciller"), ("penciller", (str,))),
|
||||||
|
(("inker", "Inker"), ("inker", (str,))),
|
||||||
|
(("colorist", "Colorist"), ("colorist", (str,))),
|
||||||
|
(("letterer", "Letterer"), ("letterer", (str,))),
|
||||||
|
(("cover_artist", "CoverArtist"), ("cover_artist", (str,))),
|
||||||
|
(("editor", "Editor"), ("editor", (str,))),
|
||||||
|
(("translator", "Translator"), ("translator", (str,))),
|
||||||
|
(("publisher", "Publisher"), ("publisher", (str,))),
|
||||||
|
(("imprint", "Imprint"), ("imprint", (str,))),
|
||||||
|
(("genre", "Genre"), ("genre", (str,))),
|
||||||
|
(("tags", "Tags"), ("tags", (str,))),
|
||||||
|
(("web", "Web"), ("web", (str,))),
|
||||||
|
(("format", "Format"), ("format", (Format, str))),
|
||||||
|
(("ean", "EAN"), ("ean", (str,))),
|
||||||
|
(("black_white", "BlackAndWhite"), ("black_white", (YesNo, str))),
|
||||||
|
(("manga", "Manga"), ("manga", (Manga, str))),
|
||||||
|
(("characters", "Characters"), ("characters", (str,))),
|
||||||
|
(("teams", "Teams"), ("teams", (str,))),
|
||||||
|
(("locations", "Locations"), ("locations", (str,))),
|
||||||
|
(("scan_information", "ScanInformation"), ("scan_information", (str,))),
|
||||||
|
(("story_arc", "StoryArc"), ("story_arc", (str,))),
|
||||||
|
(("story_arc_number", "StoryArcNumber"), ("story_arc_number", (str,))),
|
||||||
|
(("series_group", "SeriesGroup"), ("series_group", (str,))),
|
||||||
|
(("age_rating", "AgeRating"), ("age_rating", (AgeRating, str))),
|
||||||
|
(("main_character_or_team", "MainCharacterOrTeam"), ("main_character_or_team", (str,))),
|
||||||
|
(("review", "Review"), ("review", (str,))),
|
||||||
|
(("language_iso", "LanguageISO"), ("language_iso", (ValidLanguage, str))),
|
||||||
|
(("community_rating", "CommunityRating"), ("community_rating", (ValidRating, float, int, str))),
|
||||||
|
)
|
||||||
|
|||||||
+36
-1
@@ -1,4 +1,5 @@
|
|||||||
from cbz.constants import YesNo, Manga, AgeRating, Format
|
from cbz.constants import YesNo, Manga, AgeRating, Format, FIELDS
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
class ComicModel:
|
class ComicModel:
|
||||||
@@ -45,3 +46,37 @@ class ComicModel:
|
|||||||
main_character_or_team: str = ""
|
main_character_or_team: str = ""
|
||||||
review: str = ""
|
review: str = ""
|
||||||
_filepath: str = ""
|
_filepath: str = ""
|
||||||
|
|
||||||
|
def __init__(self, kwargs: dict):
|
||||||
|
"""
|
||||||
|
Set class variables from input dictionary, check types of input and cast values to correct type
|
||||||
|
:param kwargs: dictionary of input data
|
||||||
|
"""
|
||||||
|
for kwarg_key, kwarg_value in kwargs.items():
|
||||||
|
for keys, values in FIELDS:
|
||||||
|
if kwarg_key in keys:
|
||||||
|
variable, types = values
|
||||||
|
if hasattr(self, variable):
|
||||||
|
setattr(self, variable, self._check_type(kwarg_key, kwarg_value, types))
|
||||||
|
break
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _check_type(key: str, value: Any, types: tuple) -> Any:
|
||||||
|
"""
|
||||||
|
Check type of value, and cast this value to first type of the types
|
||||||
|
:param key: name of key of variable (using only for best error information)
|
||||||
|
:param value: value for a check
|
||||||
|
:param types: list of the allowed types
|
||||||
|
:return: value with new type
|
||||||
|
"""
|
||||||
|
if not isinstance(value, types):
|
||||||
|
raise ValueError(
|
||||||
|
f"Unexpected type of {key}, got: {type(value).__name__}, expected: {[i.__name__ for i in types]}")
|
||||||
|
return types[0](value)
|
||||||
|
|
||||||
|
def _get(self) -> dict:
|
||||||
|
"""
|
||||||
|
Create dictionary from variables by FIELDS
|
||||||
|
:return: dictionary with variables value
|
||||||
|
"""
|
||||||
|
return {key[1]: self._check_type(value[0], getattr(self, value[0]), value[1]) for key, value in FIELDS}
|
||||||
|
|||||||
Reference in New Issue
Block a user