@@ -7,7 +7,7 @@
|
||||
# published by the Free Software Foundation.
|
||||
|
||||
import sys
|
||||
from ..util import re_compile
|
||||
from ..text import re_compile
|
||||
|
||||
modules = [
|
||||
"2ch",
|
||||
@@ -40,6 +40,7 @@ modules = [
|
||||
"chevereto",
|
||||
"cien",
|
||||
"civitai",
|
||||
"comick",
|
||||
"comicvine",
|
||||
"cyberdrop",
|
||||
"danbooru",
|
||||
|
||||
167
gallery_dl/extractor/comick.py
Normal file
167
gallery_dl/extractor/comick.py
Normal file
@@ -0,0 +1,167 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2025 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://comick.io/"""
|
||||
|
||||
from .common import ChapterExtractor, MangaExtractor
|
||||
from .. import text
|
||||
from ..cache import memcache
|
||||
|
||||
BASE_PATTERN = r"(?:https?://)?(?:www\.)?comick\.io"
|
||||
|
||||
|
||||
class ComickBase():
|
||||
"""Base class for comick.io extractors"""
|
||||
category = "comick"
|
||||
root = "https://comick.io"
|
||||
|
||||
@memcache(keyarg=1)
|
||||
def _manga_info(self, slug):
|
||||
url = f"{self.root}/comic/{slug}"
|
||||
page = self.request(url).text
|
||||
data = self._extract_nextdata(page)
|
||||
props = data["props"]["pageProps"]
|
||||
comic = props["comic"]
|
||||
|
||||
map_status = {
|
||||
0: "Unknown",
|
||||
1: "Ongoing",
|
||||
2: "Complete",
|
||||
}
|
||||
|
||||
genre = []
|
||||
theme = []
|
||||
format = ""
|
||||
for item in comic["md_comic_md_genres"]:
|
||||
item = item["md_genres"]
|
||||
group = item["group"]
|
||||
if group == "Genre":
|
||||
genre.append(item["name"])
|
||||
elif group == "Theme":
|
||||
theme.append(item["name"])
|
||||
else:
|
||||
format = item["name"]
|
||||
|
||||
if mu := comic["mu_comics"]:
|
||||
tags = [c["mu_categories"]["title"]
|
||||
for c in mu["mu_comic_categories"]]
|
||||
publisher = [p["mu_publishers"]["title"]
|
||||
for p in mu["mu_comic_publishers"]]
|
||||
else:
|
||||
tags = publisher = ()
|
||||
|
||||
return {
|
||||
"manga": comic["title"],
|
||||
"manga_id": comic["id"],
|
||||
"manga_hid": comic["hid"],
|
||||
"manga_slug": slug,
|
||||
"manga_titles": [t["title"] for t in comic["md_titles"]],
|
||||
"artist": [a["name"] for a in props["artists"]],
|
||||
"author": [a["name"] for a in props["authors"]],
|
||||
"genre" : genre,
|
||||
"theme" : theme,
|
||||
"format": format,
|
||||
"tags" : tags,
|
||||
"publisher": publisher,
|
||||
"published": text.parse_int(comic["year"]),
|
||||
"description": comic["desc"],
|
||||
"demographic": props["demographic"],
|
||||
"origin": comic["iso639_1"],
|
||||
"mature": props["matureContent"],
|
||||
"rating": comic["content_rating"],
|
||||
"rank" : comic["follow_rank"],
|
||||
"score" : text.parse_float(comic["bayesian_rating"]),
|
||||
"status": map_status[comic["status"]],
|
||||
"links" : comic["links"],
|
||||
"_build_id": data["buildId"],
|
||||
}
|
||||
|
||||
def _chapter_info(self, manga, chstr):
|
||||
slug = manga['manga_slug']
|
||||
url = (f"{self.root}/_next/data/{manga['_build_id']}"
|
||||
f"/comic/{slug}/{chstr}.json")
|
||||
params = {"slug": slug, "chapter": chstr}
|
||||
return self.request_json(url, params=params)["pageProps"]
|
||||
|
||||
|
||||
class ComickChapterExtractor(ComickBase, ChapterExtractor):
|
||||
"""Extractor for comick.io manga chapters"""
|
||||
pattern = BASE_PATTERN + r"/comic/([\w-]+)/(\w+-chapter-[^/?#]+)"
|
||||
example = "https://comick.io/comic/MANGA/ID-chapter-123-en"
|
||||
|
||||
def __init__(self, match):
|
||||
ChapterExtractor.__init__(self, match, False)
|
||||
|
||||
def metadata(self, page):
|
||||
slug, chstr = self.groups
|
||||
manga = self._manga_info(slug)
|
||||
props = self._chapter_info(manga, chstr)
|
||||
|
||||
ch = props["chapter"]
|
||||
self._images = ch["md_images"]
|
||||
chapter, sep, minor = ch["chap"].partition(".")
|
||||
|
||||
return {
|
||||
**manga,
|
||||
"title" : props["chapTitle"],
|
||||
"volume" : text.parse_int(ch["vol"]),
|
||||
"chapter" : text.parse_int(chapter),
|
||||
"chapter_minor" : sep + minor,
|
||||
"chapter_id" : ch["id"],
|
||||
"chapter_hid" : ch["hid"],
|
||||
"chapter_string": chstr,
|
||||
"group" : ch["group_name"],
|
||||
"date" : text.parse_datetime(
|
||||
ch["created_at"][:19], "%Y-%m-%dT%H:%M:%S"),
|
||||
"date_updated" : text.parse_datetime(
|
||||
ch["updated_at"][:19], "%Y-%m-%dT%H:%M:%S"),
|
||||
"lang" : ch["lang"],
|
||||
}
|
||||
|
||||
def images(self, page):
|
||||
return [
|
||||
("https://meo.comick.pictures/" + img["b2key"], img)
|
||||
for img in self._images
|
||||
]
|
||||
|
||||
|
||||
class ComickMangaExtractor(ComickBase, MangaExtractor):
|
||||
"""Extractor for comick.io manga"""
|
||||
chapterclass = ComickChapterExtractor
|
||||
pattern = BASE_PATTERN + r"/comic/([\w-]+)/?(?:\?lang=(\w{2}))?"
|
||||
example = "https://comick.io/comic/MANGA"
|
||||
|
||||
def __init__(self, match):
|
||||
MangaExtractor.__init__(self, match, False)
|
||||
|
||||
def chapters(self, page):
|
||||
slug, lang = self.groups
|
||||
manga = self._manga_info(slug)
|
||||
|
||||
url = f"https://api.comick.io/comic/{manga['manga_hid']}/chapters"
|
||||
params = {"lang": lang}
|
||||
data = self.request_json(url, params=params)
|
||||
|
||||
if data["total"] > data["limit"]:
|
||||
# workaround for manga with more than 60 chapters
|
||||
ch = data["chapters"][0]
|
||||
chstr = f"{ch['hid']}-chapter-{ch['chap']}-{ch['lang']}"
|
||||
data = self._chapter_info(manga, chstr)
|
||||
|
||||
results = []
|
||||
for ch in data["chapters"]:
|
||||
url = (f"{self.root}/comic/{slug}"
|
||||
f"/{ch['hid']}-chapter-{ch['chap']}-{ch['lang']}")
|
||||
|
||||
ch.update(manga)
|
||||
chapter, sep, minor = ch["chap"].partition(".")
|
||||
ch["chapter"] = text.parse_int(chapter)
|
||||
ch["chapter_minor"] = sep + minor
|
||||
|
||||
results.append((url, ch))
|
||||
return results
|
||||
Reference in New Issue
Block a user