diff --git a/CHANGELOG.md b/CHANGELOG.md index 8333a42..1399a3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,16 +15,20 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - Metadata is now added to each chapter. Schema standard: [https://anansi-project.github.io/docs/comicinfo/schemas/v2.0](https://anansi-project.github.io/docs/comicinfo/schemas/v2.0) -- `xmltodict` as a package requirement +- Added `xmltodict` as a package requirement - Cache now also saves the manga title +- New tests ### Fixed - API template typos +- Some useless type annotations ### Changed - Simplified the chapter info generation +- Updated the license year +- Updated the API template ## [2.2.20] - 2023-02-12 diff --git a/LICENSE b/LICENSE index d2b1f45..08ef653 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Ivan Schaller +Copyright (c) 2021-2023 Ivan Schaller Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/contrib/api_template.py b/contrib/api_template.py index ecbbc88..2ec34fa 100644 --- a/contrib/api_template.py +++ b/contrib/api_template.py @@ -22,9 +22,10 @@ class YourAPI: api_base_url = "https://api.mangadex.org" img_base_url = "https://uploads.mangadex.org" - # get infos to initiate class def __init__(self, url_uuid, language, forcevol): - # static info + """ + get infos to initiate class + """ self.api_name = "Your API Name" self.url_uuid = url_uuid @@ -34,32 +35,49 @@ class YourAPI: # attributes needed by app.py self.manga_uuid = "abc" self.manga_title = "abc" - self.chapter_list = ["1", "2"] + self.chapter_list = ["1", "2", "2.1", "5", "10"] + self.manga_chapter_data = { # example data + "1": { + "uuid": "abc", + "volume": "1", + "chapter": "1", + "name": "test", + }, + "2": { + "uuid": "abc", + "volume": "1", + "chapter": "2", + "name": "test", + }, + } + # or with --forcevol + self.manga_chapter_data = { + "1:1": { + "uuid": "abc", + "volume": "1", + "chapter": "1", + "name": "test", + }, + "1:2": { + "uuid": "abc", + "volume": "1", + "chapter": "2", + "name": "test", + }, + } - # methods needed by app.py - # get chapter infos as a dictionary - def get_manga_chapter_data(chapter: str) -> dict: - # these keys have to be returned - return { - "1": { - "uuid": "abc", - "volume": "1", - "chapter": "1", - "name": "test", - }, - } - # or with --forcevol - return { - "1:1": { - "uuid": "abc", - "volume": "1", - "chapter": "1", - "name": "test", - }, - } - - # get chapter images as a list (full links) def get_chapter_images(chapter: str, download_wait: float) -> list: + """ + Get chapter images as a list (full links) + + Args: + chapter: The chapter number (chapter data index) + download_wait: Wait time between image downloads + + Returns: + The list of urls of the page images + """ + # example return [ "https://abc.def/image/123.png", @@ -67,8 +85,18 @@ class YourAPI: "https://abc.def/image/12345.png", ] - # get metadata with correct keys for ComicInfo.xml def create_metadata(self, chapter: str) -> dict: + """ + Get metadata with correct keys for ComicInfo.xml + Provide as much metadata as possible. empty/false values will be ignored + + Args: + chapter: The chapter number (chapter data index) + + Returns: + The metadata as a dict + """ + # example return { "Volume": "abc", diff --git a/mangadlp/api/mangadex.py b/mangadlp/api/mangadex.py index 8bb574b..4e35110 100644 --- a/mangadlp/api/mangadex.py +++ b/mangadlp/api/mangadex.py @@ -138,10 +138,9 @@ class Mangadex: "Error retrieving the chapters list. Did you specify a valid language code?" ) raise exc - else: - if total_chapters == 0: - log.error("No chapters available to download in specified language") - raise KeyError + if total_chapters == 0: + log.error("No chapters available to download in specified language") + raise KeyError log.debug(f"Total chapters={total_chapters}") return total_chapters @@ -272,9 +271,10 @@ class Mangadex: "Volume": chapter_data["volume"], "Number": chapter_data["chapter"], "PageCount": chapter_data["pages"], + "Title": chapter_data["name"], + "Series": self.manga_title, "Count": len(self.manga_chapter_data), "LanguageISO": self.language, - "Title": self.manga_title, "Summary": self.manga_data["attributes"]["description"].get("en"), "Genre": self.manga_data["attributes"].get("publicationDemographic"), "Web": f"https://mangadex.org/title/{self.manga_uuid}", diff --git a/mangadlp/metadata.py b/mangadlp/metadata.py index 651d4ae..7b3f97f 100644 --- a/mangadlp/metadata.py +++ b/mangadlp/metadata.py @@ -5,6 +5,10 @@ from loguru import logger as log def write_metadata(chapter_path: Path, metadata: dict) -> None: + if metadata["Format"] == "pdf": + log.warning("Can't add metadata for pdf format. Skipping") + return + try: metadata_template = Path("mangadlp/metadata/ComicInfo.xml").read_text( encoding="utf8" @@ -27,5 +31,6 @@ def write_metadata(chapter_path: Path, metadata: dict) -> None: log.debug(f"Updating metadata: '{key}' = '{value}'") metadata_empty["ComicInfo"][key] = value - metadata_export = xmltodict.unparse(metadata_empty, pretty=True, indent=(" " * 4)) + metadata_export = xmltodict.unparse(metadata_empty, pretty=True, indent=" " * 4) + metadata_file.touch(exist_ok=True) metadata_file.write_text(metadata_export, encoding="utf8") diff --git a/mangadlp/metadata/ComicInfo.xml b/mangadlp/metadata/ComicInfo.xml index ef77aba..0bc92af 100644 --- a/mangadlp/metadata/ComicInfo.xml +++ b/mangadlp/metadata/ComicInfo.xml @@ -1,6 +1,7 @@ + diff --git a/mangadlp/utils.py b/mangadlp/utils.py index 32c7a87..91fe552 100644 --- a/mangadlp/utils.py +++ b/mangadlp/utils.py @@ -9,7 +9,7 @@ from loguru import logger as log # create an archive of the chapter images def make_archive(chapter_path: Path, file_format: str) -> None: - zip_path: Path = Path(f"{chapter_path}.zip") + zip_path = Path(f"{chapter_path}.zip") try: # create zip with ZipFile(zip_path, "w") as zipfile: @@ -29,7 +29,7 @@ def make_pdf(chapter_path: Path) -> None: log.error("Cant import img2pdf. Please install it first") raise exc - pdf_path: Path = Path(f"{chapter_path}.pdf") + pdf_path = Path(f"{chapter_path}.pdf") images: list[str] = [] for file in chapter_path.iterdir(): images.append(str(file)) diff --git a/tests/ComicInfo_test.xml b/tests/ComicInfo_test.xml new file mode 100644 index 0000000..4c8d2a6 --- /dev/null +++ b/tests/ComicInfo_test.xml @@ -0,0 +1,20 @@ + + + title1 + series1 + 2 + 10 + 1 + summary1 + genre1 + https://mangadex.org + 99 + en + + + + Unknown + Yes + Unknown + Downloaded with https://github.com/olofvndrhr/manga-dlp + \ No newline at end of file diff --git a/tests/test_02_utils.py b/tests/test_02_utils.py index a13f277..13752b2 100644 --- a/tests/test_02_utils.py +++ b/tests/test_02_utils.py @@ -3,8 +3,7 @@ from pathlib import Path import pytest -import mangadlp.app as app -import mangadlp.utils as utils +from mangadlp import app, utils def test_make_archive_true(): diff --git a/tests/test_03_downloader.py b/tests/test_03_downloader.py index d9ea7df..8bb0c57 100644 --- a/tests/test_03_downloader.py +++ b/tests/test_03_downloader.py @@ -4,7 +4,7 @@ from pathlib import Path import pytest import requests -import mangadlp.downloader as downloader +from mangadlp import downloader def test_downloader(): diff --git a/tests/test_04_input.py b/tests/test_04_input.py index fb41fe4..093558d 100644 --- a/tests/test_04_input.py +++ b/tests/test_04_input.py @@ -1,7 +1,4 @@ import os -from pathlib import Path - -import pytest import mangadlp.cli as mdlpinput diff --git a/tests/test_06_cache.py b/tests/test_06_cache.py index b0d51e2..a3e065b 100644 --- a/tests/test_06_cache.py +++ b/tests/test_06_cache.py @@ -6,27 +6,28 @@ from mangadlp.cache import CacheDB def test_cache_creation(): cache_file = Path("cache.json") - cache = CacheDB(cache_file, "abc", "en") + cache = CacheDB(cache_file, "abc", "en", "test") - assert cache_file.exists() and cache_file.read_text(encoding="utf8") == "{}" + assert cache_file.exists() cache_file.unlink() def test_cache_insert(): cache_file = Path("cache.json") - cache = CacheDB(cache_file, "abc", "en") + cache = CacheDB(cache_file, "abc", "en", "test") cache.add_chapter("1") cache.add_chapter("2") cache_data = json.loads(cache_file.read_text(encoding="utf8")) assert cache_data["abc__en"]["chapters"] == ["1", "2"] + assert cache_data["abc__en"]["name"] == "test" cache_file.unlink() def test_cache_update(): cache_file = Path("cache.json") - cache = CacheDB(cache_file, "abc", "en") + cache = CacheDB(cache_file, "abc", "en", "test") cache.add_chapter("1") cache.add_chapter("2") @@ -43,29 +44,31 @@ def test_cache_update(): def test_cache_multiple(): cache_file = Path("cache.json") - cache1 = CacheDB(cache_file, "abc", "en") + cache1 = CacheDB(cache_file, "abc", "en", "test") cache1.add_chapter("1") cache1.add_chapter("2") - cache2 = CacheDB(cache_file, "def", "en") + cache2 = CacheDB(cache_file, "def", "en", "test2") cache2.add_chapter("8") cache2.add_chapter("9") cache_data = json.loads(cache_file.read_text(encoding="utf8")) assert cache_data["abc__en"]["chapters"] == ["1", "2"] + assert cache_data["abc__en"]["name"] == "test" assert cache_data["def__en"]["chapters"] == ["8", "9"] + assert cache_data["def__en"]["name"] == "test2" cache_file.unlink() def test_cache_lang(): cache_file = Path("cache.json") - cache1 = CacheDB(cache_file, "abc", "en") + cache1 = CacheDB(cache_file, "abc", "en", "test") cache1.add_chapter("1") cache1.add_chapter("2") - cache2 = CacheDB(cache_file, "abc", "de") + cache2 = CacheDB(cache_file, "abc", "de", "test") cache2.add_chapter("8") cache2.add_chapter("9") diff --git a/tests/test_07_metadata.py b/tests/test_07_metadata.py new file mode 100644 index 0000000..6a11324 --- /dev/null +++ b/tests/test_07_metadata.py @@ -0,0 +1,31 @@ +from pathlib import Path + +from mangadlp.metadata import write_metadata + + +def test_metadata_creation(): + test_metadata_file = Path("tests/ComicInfo_test.xml") + metadata_path = Path("tests/") + metadata_file = Path("tests/ComicInfo.xml") + metadata = { + "Volume": "1", + "Number": "2", + "PageCount": "99", + "Count": "10", + "LanguageISO": "en", + "Title": "title1", + "Series": "series1", + "Summary": "summary1", + "Genre": "genre1", + "Web": "https://mangadex.org", + } + + write_metadata(metadata_path, metadata) + assert metadata_file.exists() + + read_in_metadata = metadata_file.read_text(encoding="utf8") + test_metadata = test_metadata_file.read_text(encoding="utf8") + assert test_metadata == read_in_metadata + + # cleanup + metadata_file.unlink() diff --git a/tests/test_11_api_mangadex.py b/tests/test_11_api_mangadex.py index ad10b36..9ef1868 100644 --- a/tests/test_11_api_mangadex.py +++ b/tests/test_11_api_mangadex.py @@ -64,7 +64,7 @@ def test_chapter_infos(): language = "en" forcevol = False test = Mangadex(url_uuid, language, forcevol) - chapter_infos = test.get_chapter_infos("1") + chapter_infos = test.manga_chapter_data["1"] chapter_uuid = chapter_infos["uuid"] chapter_name = chapter_infos["name"] chapter_num = chapter_infos["chapter"] @@ -239,3 +239,24 @@ def test_get_chapter_images_error(monkeypatch): monkeypatch.setattr(requests, "get", fail_url) assert not test.get_chapter_images(chapter_num, 2) + + +def test_chapter_metadata(): + url_uuid = "https://mangadex.org/title/a96676e5-8ae2-425e-b549-7f15dd34a6d8/komi-san-wa-komyushou-desu" + language = "en" + forcevol = False + test = Mangadex(url_uuid, language, forcevol) + chapter_metadata = test.create_metadata("1") + manga_name = chapter_metadata["Series"] + chapter_name = chapter_metadata["Title"] + chapter_num = chapter_metadata["Number"] + chapter_volume = chapter_metadata["Volume"] + chapter_url = chapter_metadata["Web"] + + assert (manga_name, chapter_name, chapter_volume, chapter_num, chapter_url) == ( + "Komi-san wa Komyushou Desu", + "A Normal Person", + "1", + "1", + "https://mangadex.org/title/a96676e5-8ae2-425e-b549-7f15dd34a6d8", + ) diff --git a/tests/test_21_full.py b/tests/test_21_full.py index dfdaee0..fc05210 100644 --- a/tests/test_21_full.py +++ b/tests/test_21_full.py @@ -6,7 +6,7 @@ from pathlib import Path import pytest -import mangadlp.app as app +from mangadlp import app @pytest.fixture @@ -107,12 +107,14 @@ def test_full_with_input_folder(wait_20s): download_path = "tests" manga_path = Path("tests/Shikimori's Not Just a Cutie") chapter_path = Path("tests/Shikimori's Not Just a Cutie/Ch. 1") + metadata_path = Path("tests/Shikimori's Not Just a Cutie/ComicInfo.xml") command_args = f"-u {url_uuid} -l {language} -c {chapters} --path {download_path} --format '{file_format}' --debug --wait 2" script_path = "manga-dlp.py" os.system(f"python3 {script_path} {command_args}") assert manga_path.exists() and manga_path.is_dir() assert chapter_path.exists() and chapter_path.is_dir() + assert metadata_path.exists() and metadata_path.is_file() # cleanup shutil.rmtree(manga_path, ignore_errors=True)