diff --git a/contrib/api_template.py b/contrib/api_template.py index fa2f45c..dce31ff 100644 --- a/contrib/api_template.py +++ b/contrib/api_template.py @@ -1,4 +1,6 @@ -from typing import List, Dict, Union +from typing import Dict, List, Union + +from mangadlp.metadata import ChapterData # api template for manga-dlp @@ -37,20 +39,20 @@ class YourAPI: self.manga_uuid = "abc" self.manga_title = "abc" self.chapter_list = ["1", "2", "2.1", "5", "10"] - self.manga_chapter_data: Dict[ - str, Dict[str, Union[str, int]] - ] = { # example data + self.manga_chapter_data: Dict[str, ChapterData] = { # example data "1": { "uuid": "abc", "volume": "1", "chapter": "1", "name": "test", + "pages" 2, }, "2": { "uuid": "abc", "volume": "1", "chapter": "2", "name": "test", + "pages": 45, }, } # or with --forcevol @@ -86,7 +88,7 @@ class YourAPI: "https://abc.def/image/12345.png", ] - def create_metadata(self, chapter: str) -> Dict[str, Union[str, int, None]]: + def create_metadata(self, chapter: str) -> ComicInfo: """Get metadata with correct keys for ComicInfo.xml. Provide as much metadata as possible. empty/false values will be ignored. diff --git a/justfile b/justfile index d33455c..c8e5ee7 100755 --- a/justfile +++ b/justfile @@ -81,9 +81,6 @@ test_shfmt: test_black: @python3 -m black --check --diff mangadlp/ -test_mypy: - @python3 -m mypy --install-types --non-interactive --ignore-missing-imports mangadlp/ - test_pyright: @python3 -m pyright mangadlp/ @@ -120,7 +117,6 @@ lint: -just test_ci_conf just test_shfmt just test_black - just test_mypy just test_pyright just test_ruff @echo -e "\n\033[0;32m=== ALL DONE ===\033[0m\n" @@ -130,7 +126,6 @@ tests: -just test_ci_conf just test_shfmt just test_black - just test_mypy just test_pyright just test_ruff just test_pytest @@ -141,7 +136,6 @@ tests_full: -just test_ci_conf just test_shfmt just test_black - just test_mypy just test_pyright just test_ruff just test_build diff --git a/mangadlp/api/mangadex.py b/mangadlp/api/mangadex.py index 5b79460..0fc2380 100644 --- a/mangadlp/api/mangadex.py +++ b/mangadlp/api/mangadex.py @@ -1,11 +1,12 @@ import re from time import sleep -from typing import Any, Dict, List, Union +from typing import Any, Dict, List import requests from loguru import logger as log from mangadlp import utils +from mangadlp.metadata import ChapterData, ComicInfo class Mangadex: @@ -150,13 +151,13 @@ class Mangadex: return total_chapters # get chapter data like name, uuid etc - def get_chapter_data(self) -> Dict[str, Dict[str, Union[str, int]]]: + def get_chapter_data(self) -> Dict[str, ChapterData]: log.debug(f"Getting chapter data for: {self.manga_uuid}") api_sorting = "order[chapter]=asc&order[volume]=asc" # check for chapters in specified lang total_chapters = self.check_chapter_lang() - chapter_data = {} + chapter_data: dict[str, ChapterData] = {} last_volume, last_chapter = ("", "") offset = 0 while offset < total_chapters: # if more than 500 chapters @@ -205,7 +206,7 @@ class Mangadex: # increase offset for mangas with more than 500 chapters offset += 500 - return chapter_data # type:ignore + return chapter_data # get images for the chapter (mangadex@home) def get_chapter_images(self, chapter: str, wait_time: float) -> List[str]: @@ -259,8 +260,8 @@ class Mangadex: log.debug(f"Creating chapter list for: {self.manga_uuid}") chapter_list: List[str] = [] for data in self.manga_chapter_data.values(): - chapter_number: str = data["chapter"] # type:ignore - volume_number: str = data["volume"] # type:ignore + chapter_number: str = data["chapter"] + volume_number: str = data["volume"] if self.forcevol: chapter_list.append(f"{volume_number}:{chapter_number}") else: @@ -268,7 +269,7 @@ class Mangadex: return chapter_list - def create_metadata(self, chapter: str) -> Dict[str, Union[str, int, None]]: + def create_metadata(self, chapter: str) -> ComicInfo: log.info("Creating metadata from api") chapter_data = self.manga_chapter_data[chapter] @@ -276,7 +277,7 @@ class Mangadex: volume = int(chapter_data["volume"]) except (ValueError, TypeError): volume = None - metadata = { + metadata: ComicInfo = { "Volume": volume, "Number": chapter_data.get("chapter"), "PageCount": chapter_data.get("pages"), @@ -289,4 +290,4 @@ class Mangadex: "Web": f"https://mangadex.org/title/{self.manga_uuid}", } - return metadata # pyright:ignore + return metadata diff --git a/mangadlp/app.py b/mangadlp/app.py index 4b8ad91..7aa7fec 100644 --- a/mangadlp/app.py +++ b/mangadlp/app.py @@ -9,7 +9,7 @@ from mangadlp import downloader, utils from mangadlp.api.mangadex import Mangadex from mangadlp.cache import CacheDB from mangadlp.hooks import run_hook -from mangadlp.metadata import write_metadata +from mangadlp.metadata import ChapterData, write_metadata from mangadlp.utils import get_file_format @@ -310,7 +310,7 @@ class MangaDLP: # once called per chapter def get_chapter(self, chapter: str) -> Path: # get chapter infos - chapter_infos: Dict[str, Union[str, int]] = self.api.manga_chapter_data[chapter] + chapter_infos: ChapterData = self.api.manga_chapter_data[chapter] log.debug(f"Chapter infos: {chapter_infos}") # get image urls for chapter @@ -342,8 +342,8 @@ class MangaDLP: # get filename for chapter (without suffix) chapter_filename = utils.get_filename( self.manga_title, - chapter_infos["name"], # type:ignore - chapter_infos["volume"], # type:ignore + chapter_infos["name"], + chapter_infos["volume"], chapter, self.forcevol, self.name_format, diff --git a/mangadlp/metadata.py b/mangadlp/metadata.py index 9ee1478..78eea57 100644 --- a/mangadlp/metadata.py +++ b/mangadlp/metadata.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Any, Dict, Tuple, Union +from typing import Any, Dict, Optional, Tuple, TypedDict, Union import xmltodict from loguru import logger as log @@ -59,12 +59,59 @@ METADATA_TYPES: Dict[str, Tuple[type, Any, list[Union[str, int]]]] = { } -def validate_metadata( - metadata_in: Dict[str, Union[str, int]] -) -> Dict[str, Dict[str, Union[str, int]]]: +class ComicInfo(TypedDict, total=False): + """ComicInfo.xml basic types. + + Validation is done via metadata.validate_metadata() + All valid types and values are specified in metadata.METADATA_TYPES + """ + + Title: Optional[str] + Series: Optional[str] + Number: Optional[str] + Count: Optional[int] + Volume: Optional[int] + AlternateSeries: Optional[str] + AlternateNumber: Optional[str] + AlternateCount: Optional[int] + Summary: Optional[str] + Notes: Optional[str] + Year: Optional[int] + Month: Optional[int] + Day: Optional[int] + Writer: Optional[str] + Colorist: Optional[str] + Publisher: Optional[str] + Genre: Optional[str] + Web: Optional[str] + PageCount: Optional[int] + LanguageISO: Optional[str] + Format: Optional[str] + BlackAndWhite: Optional[str] + Manga: Optional[str] + ScanInformation: Optional[str] + SeriesGroup: Optional[str] + AgeRating: Optional[str] + CommunityRating: Optional[int] + + +class ChapterData(TypedDict): + """Basic chapter-data types. + + All values have to be provided. + """ + + uuid: str + volume: str + chapter: str + name: str + pages: int + + +def validate_metadata(metadata_in: ComicInfo) -> Dict[str, ComicInfo]: log.info("Validating metadata") - metadata_valid: dict[str, Dict[str, Union[str, int]]] = {"ComicInfo": {}} + metadata_valid: dict[str, ComicInfo] = {"ComicInfo": {}} for key, value in METADATA_TYPES.items(): metadata_type, metadata_default, metadata_validation = value @@ -77,7 +124,7 @@ def validate_metadata( # check if metadata key is available try: - md_to_check = metadata_in[key] + md_to_check: Union[str, int, None] = metadata_in[key] except KeyError: continue # check if provided metadata item is empty @@ -106,8 +153,8 @@ def validate_metadata( return metadata_valid -def write_metadata(chapter_path: Path, metadata: Dict[str, Union[str, int]]) -> None: - if metadata["Format"] == "pdf": +def write_metadata(chapter_path: Path, metadata: ComicInfo) -> None: + if metadata["Format"] == "pdf": # pyright:ignore log.warning("Can't add metadata for pdf format. Skipping") return