From 7b2bcf68a5aa63c64c3476a237e738bd713ad5a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20F=C3=A4hrmann?= Date: Mon, 28 Apr 2025 22:12:19 +0200 Subject: [PATCH] [manganelo] support 'nelomanga.net' and mirror domains (#7423) - natomanga.com - nelomanga.net - manganato.gg - mangakakalot.gg --- docs/supportedsites.md | 40 ++++-- gallery_dl/extractor/__init__.py | 1 - gallery_dl/extractor/mangakakalot.py | 92 -------------- gallery_dl/extractor/manganelo.py | 174 ++++++++++++++------------- scripts/supportedsites.py | 5 +- test/results/mangakakalot.py | 59 ++++++--- test/results/manganato.py | 63 ++++++++++ test/results/manganelo.py | 104 ---------------- test/results/natomanga.py | 63 ++++++++++ test/results/nelomanga.py | 63 ++++++++++ 10 files changed, 355 insertions(+), 309 deletions(-) delete mode 100644 gallery_dl/extractor/mangakakalot.py create mode 100644 test/results/manganato.py delete mode 100644 test/results/manganelo.py create mode 100644 test/results/natomanga.py create mode 100644 test/results/nelomanga.py diff --git a/docs/supportedsites.md b/docs/supportedsites.md index 67b93d25..54020c81 100644 --- a/docs/supportedsites.md +++ b/docs/supportedsites.md @@ -565,24 +565,12 @@ Consider all listed sites to potentially be NSFW. Authors, Chapters, Followed Feed, Lists, Manga Supported - - MangaKakalot - https://mangakakalot.tv/ - Chapters, Manga - - MangaLife https://manga4life.com/ Chapters, Manga - - Manganato - https://manganato.com/ - Chapters, Manga - - MangaPark https://mangapark.net/ @@ -1408,6 +1396,34 @@ Consider all listed sites to potentially be NSFW. + + MangaNelo and Mirror Sites + + + MangaNelo + https://www.nelomanga.net/ + Chapters, Manga + + + + MangaNato + https://www.natomanga.com/ + Chapters, Manga + + + + MangaNato + https://www.manganato.gg/ + Chapters, Manga + + + + MangaKakalot + https://www.mangakakalot.gg/ + Chapters, Manga + + + Misskey Instances diff --git a/gallery_dl/extractor/__init__.py b/gallery_dl/extractor/__init__.py index 9a7ca535..2da471e0 100644 --- a/gallery_dl/extractor/__init__.py +++ b/gallery_dl/extractor/__init__.py @@ -105,7 +105,6 @@ modules = [ "mangadex", "mangafox", "mangahere", - "mangakakalot", "manganelo", "mangapark", "mangaread", diff --git a/gallery_dl/extractor/mangakakalot.py b/gallery_dl/extractor/mangakakalot.py deleted file mode 100644 index 9fc8681d..00000000 --- a/gallery_dl/extractor/mangakakalot.py +++ /dev/null @@ -1,92 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright 2020 Jake Mannens -# Copyright 2021-2023 Mike Fährmann -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. - -"""Extractors for https://mangakakalot.tv/""" - -from .common import ChapterExtractor, MangaExtractor -from .. import text -import re - -BASE_PATTERN = r"(?:https?://)?(?:ww[\dw]?\.)?mangakakalot\.tv" - - -class MangakakalotBase(): - """Base class for mangakakalot extractors""" - category = "mangakakalot" - root = "https://ww8.mangakakalot.tv" - - -class MangakakalotChapterExtractor(MangakakalotBase, ChapterExtractor): - """Extractor for manga chapters from mangakakalot.tv""" - pattern = BASE_PATTERN + r"(/chapter/[^/?#]+/chapter[_-][^/?#]+)" - example = "https://ww6.mangakakalot.tv/chapter/manga-ID/chapter-01" - - def __init__(self, match): - self.path = match.group(1) - ChapterExtractor.__init__(self, match, self.root + self.path) - - def metadata(self, page): - _ , pos = text.extract(page, '', '<') - manga , pos = text.extract(page, '', '<', pos) - info , pos = text.extract(page, '', '<', pos) - author, pos = text.extract(page, '. Author:', ' already has ', pos) - - match = re.match( - r"(?:[Vv]ol\. *(\d+) )?" - r"[Cc]hapter *([^:]*)" - r"(?:: *(.+))?", info or "") - volume, chapter, title = match.groups() if match else ("", "", info) - chapter, sep, minor = chapter.partition(".") - - return { - "manga" : text.unescape(manga), - "title" : text.unescape(title) if title else "", - "author" : text.unescape(author).strip() if author else "", - "volume" : text.parse_int(volume), - "chapter" : text.parse_int(chapter), - "chapter_minor": sep + minor, - "lang" : "en", - "language" : "English", - } - - def images(self, page): - return [ - (url, None) - for url in text.extract_iter(page, '", "<") - author, pos = text.extract(page, "
  • Author(s) :", "", pos) - data["author"] = text.remove_html(author) - - results = [] - for chapter in text.extract_iter(page, '
    ', '
    '): - url, pos = text.extract(chapter, '', '', pos) - data["title"] = title.partition(": ")[2] - data["date"] , pos = text.extract( - chapter, '") - manga = extr('title="', '"') - info = extr('title="', '"') - author = extr("- Author(s) : ", "

    ") - return self._parse_chapter( - info, text.unescape(manga), text.unescape(author)) + data = { + "date" : text.parse_datetime(extr( + '"datePublished": "', '"')[:19], "%Y-%m-%dT%H:%M:%S"), + "date_updated": text.parse_datetime(extr( + '"dateModified": "', '"')[:19], "%Y-%m-%dT%H:%M:%S"), + "manga_id" : text.parse_int(extr("comic_id =", ";")), + "chapter_id" : text.parse_int(extr("chapter_id =", ";")), + "manga" : extr("comic_name =", ";").strip('" '), + "lang" : "en", + "language" : "English", + } + + chapter_name = extr("chapter_name =", ";").strip('" ') + chapter, sep, minor = chapter_name.rpartition(" ")[2].partition(".") + data["chapter"] = text.parse_int(chapter) + data["chapter_minor"] = sep + minor + data["author"] = extr(". Author:", " already has ").strip() + + return data def images(self, page): - page = text.extr( - page, 'class="container-chapter-reader', 'class="container') + extr = text.extract_from(page) + cdns = util.json_loads(extr("var cdns =", ";"))[0] + imgs = util.json_loads(extr("var chapterImages =", ";")) + + if cdns[-1] != "/": + cdns += "/" + return [ - (url, None) - for url in text.extract_iter(page, '", "<")) + author = text.remove_html(text.extr(page, "
  • Author(s) :", "")) + results = [] - append = results.append + for chapter in text.extract_iter(page, '
    ', '
    '): + url, pos = text.extract(chapter, '', '', pos) + date, pos = text.extract(chapter, '", "<")) - author = text.remove_html(extr("Author(s) :", "")) - - extr('class="row-content-chapter', '') - while True: - url = extr('class="chapter-name text-nowrap" href="', '"') - if not url: - return results - info = extr(">", "<") - date = extr('class="chapter-time text-nowrap" title="', '"') - append((url, self._parse_chapter(info, manga, author, date))) + if url[0] == "/": + url = self.root + url + results.append((url, { + "manga" : manga, + "author" : author, + "chapter" : text.parse_int(chapter), + "chapter_minor": (sep and ".") + minor, + "title" : title.partition(": ")[2], + "date" : text.parse_datetime(date, "%b-%d-%Y %H:%M"), + "lang" : "en", + "language": "English", + })) + return results diff --git a/scripts/supportedsites.py b/scripts/supportedsites.py index fb85fa2b..7fbf3ee5 100755 --- a/scripts/supportedsites.py +++ b/scripts/supportedsites.py @@ -101,7 +101,7 @@ CATEGORY_MAP = { "mangahere" : "Manga Here", "mangakakalot" : "MangaKakalot", "mangalife" : "MangaLife", - "manganelo" : "Manganato", + "manganato" : "MangaNato", "mangapark" : "MangaPark", "mangaread" : "MangaRead", "mangasee" : "MangaSee", @@ -111,7 +111,9 @@ CATEGORY_MAP = { "micmicidol" : "MIC MIC IDOL", "myhentaigallery": "My Hentai Gallery", "myportfolio" : "Adobe Portfolio", + "natomanga" : "MangaNato", "naverwebtoon" : "NaverWebtoon", + "nelomanga" : "MangaNelo", "nhentai" : "nhentai", "nijie" : "nijie", "nozomi" : "Nozomi.la", @@ -401,6 +403,7 @@ BASE_MAP = { "jschan" : "jschan Imageboards", "lolisafe" : "lolisafe and chibisafe", "lynxchan" : "LynxChan Imageboards", + "manganelo" : "MangaNelo and Mirror Sites", "moebooru" : "Moebooru and MyImouto", "szurubooru" : "szurubooru Instances", "urlshortener": "URL Shorteners", diff --git a/test/results/mangakakalot.py b/test/results/mangakakalot.py index b0b8bada..a2efceff 100644 --- a/test/results/mangakakalot.py +++ b/test/results/mangakakalot.py @@ -4,37 +4,60 @@ # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. -from gallery_dl.extractor import mangakakalot +from gallery_dl.extractor import manganelo __tests__ = ( { - "#url" : "https://ww3.mangakakalot.tv/chapter/manga-jk986845/chapter-34.2", - "#category": ("", "mangakakalot", "chapter"), - "#class" : mangakakalot.MangakakalotChapterExtractor, - "#pattern" : r"https://cm\.blazefast\.co/[0-9a-f]{2}/[0-9a-f]{2}/[0-9a-f]{32}\.jpg", - "#count" : 9, - "#sha1_metadata": "0f1586ff52f0f9cbbb25306ae64ab718f8a6a633", + "#url" : "https://www.mangakakalot.gg/manga/danzai-sareta-akuyaku-reijou-wa-gyakkou-shite-kanpeki-na-akujo-wo-mezasu/chapter-4-5", + "#category": ("manganelo", "mangakakalot", "chapter"), + "#class" : manganelo.ManganeloChapterExtractor, + "#pattern" : r"https://imgs-2.2xstorage.com/danzai-sareta-akuyaku-reijou-wa-gyakkou-shite-kanpeki-na-akujo-wo-mezasu/4\.5/\d+\.webp", + "#count" : 24, + + "author" : "NARAYAMA Bakufu", + "chapter" : 4, + "chapter_id" : 6, + "chapter_minor": ".5", + "count" : 24, + "date" : "dt:2025-04-29 16:08:07", + "date_updated" : "dt:2025-04-29 16:08:07", + "extension" : "webp", + "filename" : str, + "lang" : "en", + "language" : "English", + "manga" : "Danzai sareta Akuyaku Reijou wa, Gyakkou shite Kanpeki na Akujo wo Mezasu", + "manga_id" : 32842, + "page" : range(1, 24), }, { - "#url" : "https://mangakakalot.tv/chapter/hatarakanai_futari_the_jobless_siblings/chapter_20.1", - "#category": ("", "mangakakalot", "chapter"), - "#class" : mangakakalot.MangakakalotChapterExtractor, + "#url" : "https://mangakakalot.gg/manga/aria/chapter-60-2", + "#category": ("manganelo", "mangakakalot", "chapter"), + "#class" : manganelo.ManganeloChapterExtractor, }, { - "#url" : "https://ww3.mangakakalot.tv/manga/manga-jk986845", - "#category": ("", "mangakakalot", "manga"), - "#class" : mangakakalot.MangakakalotMangaExtractor, - "#pattern" : mangakakalot.MangakakalotChapterExtractor.pattern, - "#count" : ">= 30", + "#url" : "https://www.mangakakalot.gg/manga/aria", + "#category": ("manganelo", "mangakakalot", "manga"), + "#class" : manganelo.ManganeloMangaExtractor, + "#pattern" : manganelo.ManganeloChapterExtractor.pattern, + "#count" : 70, + + "author" : "Amano Kozue", + "chapter" : range(1, 60), + "chapter_minor": {"", ".1", ".2", ".5"}, + "date" : "type:datetime", + "lang" : "en", + "language": "English", + "manga" : "Aria", + "title" : "", }, { - "#url" : "https://mangakakalot.tv/manga/lk921810", - "#category": ("", "mangakakalot", "manga"), - "#class" : mangakakalot.MangakakalotMangaExtractor, + "#url" : "https://mangakakalot.gg/manga/aria", + "#category": ("manganelo", "mangakakalot", "manga"), + "#class" : manganelo.ManganeloMangaExtractor, }, ) diff --git a/test/results/manganato.py b/test/results/manganato.py new file mode 100644 index 00000000..52a7d174 --- /dev/null +++ b/test/results/manganato.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. + +from gallery_dl.extractor import manganelo + + +__tests__ = ( +{ + "#url" : "https://www.manganato.gg/manga/danzai-sareta-akuyaku-reijou-wa-gyakkou-shite-kanpeki-na-akujo-wo-mezasu/chapter-4-5", + "#category": ("manganelo", "manganato", "chapter"), + "#class" : manganelo.ManganeloChapterExtractor, + "#pattern" : r"https://imgs-2.2xstorage.com/danzai-sareta-akuyaku-reijou-wa-gyakkou-shite-kanpeki-na-akujo-wo-mezasu/4\.5/\d+\.webp", + "#count" : 24, + + "author" : "NARAYAMA Bakufu", + "chapter" : 4, + "chapter_id" : 6, + "chapter_minor": ".5", + "count" : 24, + "date" : "", + "date_updated" : "", + "extension" : "webp", + "filename" : str, + "lang" : "en", + "language" : "English", + "manga" : "Danzai sareta Akuyaku Reijou wa, Gyakkou shite Kanpeki na Akujo wo Mezasu", + "manga_id" : 32842, + "page" : range(1, 24), +}, + +{ + "#url" : "https://manganato.gg/manga/aria/chapter-60-2", + "#category": ("manganelo", "manganato", "chapter"), + "#class" : manganelo.ManganeloChapterExtractor, +}, + +{ + "#url" : "https://www.manganato.gg/manga/aria", + "#category": ("manganelo", "manganato", "manga"), + "#class" : manganelo.ManganeloMangaExtractor, + "#pattern" : manganelo.ManganeloChapterExtractor.pattern, + "#count" : 70, + + "author" : "Amano Kozue", + "chapter" : range(1, 60), + "chapter_minor": {"", ".1", ".2", ".5"}, + "date" : "type:datetime", + "lang" : "en", + "language": "English", + "manga" : "Aria", + "title" : "", +}, + +{ + "#url" : "https://manganato.gg/manga/aria", + "#category": ("manganelo", "manganato", "manga"), + "#class" : manganelo.ManganeloMangaExtractor, +}, + +) diff --git a/test/results/manganelo.py b/test/results/manganelo.py deleted file mode 100644 index 7236e47c..00000000 --- a/test/results/manganelo.py +++ /dev/null @@ -1,104 +0,0 @@ -# -*- coding: utf-8 -*- - -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. - -from gallery_dl.extractor import manganelo - - -__tests__ = ( -{ - "#url" : "https://chapmanganato.com/manga-gn983696/chapter-23", - "#category": ("", "manganelo", "chapter"), - "#class" : manganelo.ManganeloChapterExtractor, - "#pattern" : r"https://v\d+\.mkklcdnv6tempv5\.com/img/tab_17/03/23/39/gn983696/vol_3_chapter_23_24_yen/\d+-[no]\.jpg", - "#count" : 25, - - "author" : "Ei Yuzuki,Maki Hayashi", - "chapter" : 23, - "chapter_minor": "", - "count" : 25, - "date" : None, - "extension": "jpg", - "filename" : str, - "lang" : "en", - "language" : "English", - "manga" : "By Spring", - "page" : range(1, 25), - "title" : "24 Yen", - "volume" : 3, -}, - -{ - "#url" : "https://chapmanganelo.com/manga-ti107776/chapter-4", - "#category": ("", "manganelo", "chapter"), - "#class" : manganelo.ManganeloChapterExtractor, - "#pattern" : r"https://v\d+\.mkklcdnv6tempv5\.com/img/tab_17/01/92/08/ti970565/chapter_4_caster/\d+-o\.jpg", - "#count" : 45, - "#sha1_metadata": "06e01fa9b3fc9b5b954c0d4a98f0153b40922ded", -}, - -{ - "#url" : "https://chapmanganato.com/manga-no991297/chapter-8", - "#category": ("", "manganelo", "chapter"), - "#class" : manganelo.ManganeloChapterExtractor, - "#count" : 20, - - "chapter" : 8, - "chapter_minor": "-1", -}, - -{ - "#url" : "https://readmanganato.com/manga-gn983696/chapter-23", - "#category": ("", "manganelo", "chapter"), - "#class" : manganelo.ManganeloChapterExtractor, -}, - -{ - "#url" : "https://manganelo.com/chapter/gamers/chapter_15", - "#category": ("", "manganelo", "chapter"), - "#class" : manganelo.ManganeloChapterExtractor, -}, - -{ - "#url" : "https://manganelo.com/chapter/gq921227/chapter_23", - "#category": ("", "manganelo", "chapter"), - "#class" : manganelo.ManganeloChapterExtractor, -}, - -{ - "#url" : "https://chapmanganato.com/manga-gn983696", - "#category": ("", "manganelo", "manga"), - "#class" : manganelo.ManganeloMangaExtractor, - "#pattern" : manganelo.ManganeloChapterExtractor.pattern, - "#count" : ">= 25", -}, - -{ - "#url" : "https://m.manganelo.com/manga-ti107776", - "#category": ("", "manganelo", "manga"), - "#class" : manganelo.ManganeloMangaExtractor, - "#pattern" : manganelo.ManganeloChapterExtractor.pattern, - "#count" : ">= 12", -}, - -{ - "#url" : "https://readmanganato.com/manga-gn983696", - "#category": ("", "manganelo", "manga"), - "#class" : manganelo.ManganeloMangaExtractor, -}, - -{ - "#url" : "https://manganelo.com/manga/read_otome_no_teikoku", - "#category": ("", "manganelo", "manga"), - "#class" : manganelo.ManganeloMangaExtractor, -}, - -{ - "#url" : "https://manganelo.com/manga/ol921234/", - "#category": ("", "manganelo", "manga"), - "#class" : manganelo.ManganeloMangaExtractor, -}, - -) diff --git a/test/results/natomanga.py b/test/results/natomanga.py new file mode 100644 index 00000000..461adf71 --- /dev/null +++ b/test/results/natomanga.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. + +from gallery_dl.extractor import manganelo + + +__tests__ = ( +{ + "#url" : "https://www.natomanga.com/manga/danzai-sareta-akuyaku-reijou-wa-gyakkou-shite-kanpeki-na-akujo-wo-mezasu/chapter-4-5", + "#category": ("manganelo", "natomanga", "chapter"), + "#class" : manganelo.ManganeloChapterExtractor, + "#pattern" : r"https://imgs-2.2xstorage.com/danzai-sareta-akuyaku-reijou-wa-gyakkou-shite-kanpeki-na-akujo-wo-mezasu/4\.5/\d+\.webp", + "#count" : 24, + + "author" : "NARAYAMA Bakufu", + "chapter" : 4, + "chapter_id" : 6, + "chapter_minor": ".5", + "count" : 24, + "date" : "dt:2025-04-29 16:08:07", + "date_updated" : "dt:2025-04-29 16:08:07", + "extension" : "webp", + "filename" : str, + "lang" : "en", + "language" : "English", + "manga" : "Danzai sareta Akuyaku Reijou wa, Gyakkou shite Kanpeki na Akujo wo Mezasu", + "manga_id" : 32842, + "page" : range(1, 24), +}, + +{ + "#url" : "https://natomanga.com/manga/aria/chapter-60-2", + "#category": ("manganelo", "natomanga", "chapter"), + "#class" : manganelo.ManganeloChapterExtractor, +}, + +{ + "#url" : "https://www.natomanga.com/manga/aria", + "#category": ("manganelo", "natomanga", "manga"), + "#class" : manganelo.ManganeloMangaExtractor, + "#pattern" : manganelo.ManganeloChapterExtractor.pattern, + "#count" : 70, + + "author" : "Amano Kozue", + "chapter" : range(1, 60), + "chapter_minor": {"", ".1", ".2", ".5"}, + "date" : "type:datetime", + "lang" : "en", + "language": "English", + "manga" : "Aria", + "title" : "", +}, + +{ + "#url" : "https://natomanga.com/manga/aria", + "#category": ("manganelo", "natomanga", "manga"), + "#class" : manganelo.ManganeloMangaExtractor, +}, + +) diff --git a/test/results/nelomanga.py b/test/results/nelomanga.py new file mode 100644 index 00000000..da794fda --- /dev/null +++ b/test/results/nelomanga.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. + +from gallery_dl.extractor import manganelo + + +__tests__ = ( +{ + "#url" : "https://www.nelomanga.net/manga/danzai-sareta-akuyaku-reijou-wa-gyakkou-shite-kanpeki-na-akujo-wo-mezasu/chapter-4-5", + "#category": ("manganelo", "nelomanga", "chapter"), + "#class" : manganelo.ManganeloChapterExtractor, + "#pattern" : r"https://imgs-2.2xstorage.com/danzai-sareta-akuyaku-reijou-wa-gyakkou-shite-kanpeki-na-akujo-wo-mezasu/4\.5/\d+\.webp", + "#count" : 24, + + "author" : "NARAYAMA Bakufu", + "chapter" : 4, + "chapter_id" : 6, + "chapter_minor": ".5", + "count" : 24, + "date" : "", + "date_updated" : "", + "extension" : "webp", + "filename" : str, + "lang" : "en", + "language" : "English", + "manga" : "Danzai sareta Akuyaku Reijou wa, Gyakkou shite Kanpeki na Akujo wo Mezasu", + "manga_id" : 32842, + "page" : range(1, 24), +}, + +{ + "#url" : "https://nelomanga.net/manga/aria/chapter-60-2", + "#category": ("manganelo", "nelomanga", "chapter"), + "#class" : manganelo.ManganeloChapterExtractor, +}, + +{ + "#url" : "https://www.nelomanga.net/manga/aria", + "#category": ("manganelo", "nelomanga", "manga"), + "#class" : manganelo.ManganeloMangaExtractor, + "#pattern" : manganelo.ManganeloChapterExtractor.pattern, + "#count" : 70, + + "author" : "Amano Kozue", + "chapter" : range(1, 60), + "chapter_minor": {"", ".1", ".2", ".5"}, + "date" : "type:datetime", + "lang" : "en", + "language": "English", + "manga" : "Aria", + "title" : "", +}, + +{ + "#url" : "https://nelomanga.net/manga/aria", + "#category": ("manganelo", "nelomanga", "manga"), + "#class" : manganelo.ManganeloMangaExtractor, +}, + +)