add typed dicts for type hinting
All checks were successful
ci/woodpecker/push/tests Pipeline was successful
All checks were successful
ci/woodpecker/push/tests Pipeline was successful
This commit is contained in:
parent
e2f0a8b41f
commit
bde2b9ebe9
5 changed files with 76 additions and 32 deletions
|
@ -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.
|
||||
|
|
6
justfile
6
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue