[mangadex] add 'covers' extractor (#4994)
This commit is contained in:
@@ -592,7 +592,7 @@ Consider all listed sites to potentially be NSFW.
|
||||
<tr id="mangadex" title="mangadex">
|
||||
<td>MangaDex</td>
|
||||
<td>https://mangadex.org/</td>
|
||||
<td>Authors, Chapters, Updates Feed, Library, MDLists, Manga</td>
|
||||
<td>Authors, Chapters, Covers, Updates Feed, Library, MDLists, Manga</td>
|
||||
<td>Supported</td>
|
||||
</tr>
|
||||
<tr id="mangapark" title="mangapark">
|
||||
|
||||
@@ -96,6 +96,57 @@ class MangadexExtractor(Extractor):
|
||||
return data
|
||||
|
||||
|
||||
class MangadexCoversExtractor(MangadexExtractor):
|
||||
"""Extractor for mangadex manga covers"""
|
||||
subcategory = "covers"
|
||||
directory_fmt = ("{category}", "{manga}", "Covers")
|
||||
filename_fmt = "{volume:>02}_{lang}.{extension}"
|
||||
archive_fmt = "c_{cover_id}"
|
||||
pattern = (rf"{BASE_PATTERN}/(?:title|manga)/(?!follows|feed$)([0-9a-f-]+)"
|
||||
r"(?:/[^/?#]+)?\?tab=art")
|
||||
example = ("https://mangadex.org/title"
|
||||
"/01234567-89ab-cdef-0123-456789abcdef?tab=art")
|
||||
|
||||
def items(self):
|
||||
base = f"{self.root}/covers/{self.uuid}/"
|
||||
for cover in self.api.covers_manga(self.uuid):
|
||||
data = self._transform_cover(cover)
|
||||
name = data["cover"]
|
||||
text.nameext_from_url(name, data)
|
||||
data["cover_id"] = data["filename"]
|
||||
yield Message.Directory, data
|
||||
yield Message.Url, f"{base}{name}", data
|
||||
|
||||
def _transform_cover(self, cover):
|
||||
relationships = defaultdict(list)
|
||||
for item in cover["relationships"]:
|
||||
relationships[item["type"]].append(item)
|
||||
manga = self.api.manga(relationships["manga"][0]["id"])
|
||||
for item in manga["relationships"]:
|
||||
relationships[item["type"]].append(item)
|
||||
|
||||
cattributes = cover["attributes"]
|
||||
mattributes = manga["attributes"]
|
||||
|
||||
return {
|
||||
"manga" : (mattributes["title"].get("en") or
|
||||
next(iter(mattributes["title"].values()))),
|
||||
"manga_id": manga["id"],
|
||||
"status" : mattributes["status"],
|
||||
"author" : [author["attributes"]["name"]
|
||||
for author in relationships["author"]],
|
||||
"artist" : [artist["attributes"]["name"]
|
||||
for artist in relationships["artist"]],
|
||||
"tags" : [tag["attributes"]["name"]["en"]
|
||||
for tag in mattributes["tags"]],
|
||||
"cover" : cattributes["fileName"],
|
||||
"lang" : cattributes.get("locale"),
|
||||
"volume" : text.parse_int(cattributes["volume"]),
|
||||
"date" : text.parse_datetime(cattributes["createdAt"]),
|
||||
"date_updated": text.parse_datetime(cattributes["updatedAt"]),
|
||||
}
|
||||
|
||||
|
||||
class MangadexChapterExtractor(MangadexExtractor):
|
||||
"""Extractor for manga-chapters from mangadex.org"""
|
||||
subcategory = "chapter"
|
||||
@@ -239,6 +290,10 @@ class MangadexAPI():
|
||||
params = {"includes[]": ("scanlation_group",)}
|
||||
return self._call("/chapter/" + uuid, params)["data"]
|
||||
|
||||
def covers_manga(self, uuid):
|
||||
params = {"manga[]": uuid}
|
||||
return self._pagination_covers("/cover", params)
|
||||
|
||||
def list(self, uuid):
|
||||
return self._call("/list/" + uuid, None, True)["data"]
|
||||
|
||||
@@ -374,6 +429,20 @@ class MangadexAPI():
|
||||
|
||||
return self._pagination(endpoint, params, auth)
|
||||
|
||||
def _pagination_covers(self, endpoint, params=None, auth=False):
|
||||
if params is None:
|
||||
params = {}
|
||||
|
||||
lang = self.extractor.config("lang")
|
||||
if isinstance(lang, str) and "," in lang:
|
||||
lang = lang.split(",")
|
||||
params["locales"] = lang
|
||||
params["contentRating"] = None
|
||||
params["order[volume]"] = \
|
||||
"desc" if self.extractor.config("chapter-reverse") else "asc"
|
||||
|
||||
return self._pagination(endpoint, params, auth)
|
||||
|
||||
def _pagination(self, endpoint, params, auth=False):
|
||||
config = self.extractor.config
|
||||
|
||||
|
||||
@@ -167,4 +167,63 @@ __tests__ = (
|
||||
"#count" : ">= 15",
|
||||
},
|
||||
|
||||
{
|
||||
"#url" : "https://mangadex.org/title/f90c4398-8aad-4f51-8a1f-024ca09fdcbc?tab=art",
|
||||
"#class" : mangadex.MangadexCoversExtractor,
|
||||
"#results" : "https://mangadex.org/covers/f90c4398-8aad-4f51-8a1f-024ca09fdcbc/af3c1690-1e06-4432-909e-3e0f9ee01f68.jpg",
|
||||
|
||||
"artist" : ["Arakawa Hiromu"],
|
||||
"author" : ["Arakawa Hiromu"],
|
||||
"cover" : "af3c1690-1e06-4432-909e-3e0f9ee01f68.jpg",
|
||||
"cover_id" : "af3c1690-1e06-4432-909e-3e0f9ee01f68",
|
||||
"date" : "dt:2021-05-24 17:19:13",
|
||||
"date_updated": "dt:2021-05-24 17:19:13",
|
||||
"extension" : "jpg",
|
||||
"filename" : "af3c1690-1e06-4432-909e-3e0f9ee01f68",
|
||||
"lang" : "ja",
|
||||
"manga" : "Souten no Koumori",
|
||||
"manga_id" : "f90c4398-8aad-4f51-8a1f-024ca09fdcbc",
|
||||
"status" : "completed",
|
||||
"volume" : 0,
|
||||
"tags" : [
|
||||
"Oneshot",
|
||||
"Historical",
|
||||
"Action",
|
||||
"Martial Arts",
|
||||
"Drama",
|
||||
"Tragedy",
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
"#url" : "https://mangadex.org/title/192aa767-2479-42c1-9780-8d65a2efd36a/gachiakuta?tab=art",
|
||||
"#class" : mangadex.MangadexCoversExtractor,
|
||||
"#pattern" : r"https://mangadex\.org/covers/192aa767-2479-42c1-9780-8d65a2efd36a/[\w-]+\.jpg",
|
||||
"#count" : 19,
|
||||
|
||||
"artist" : ["Urana Kei"],
|
||||
"author" : ["Urana Kei"],
|
||||
"cover_id" : "iso:uuid",
|
||||
"date" : "type:datetime",
|
||||
"date_updated": "type:datetime",
|
||||
"extension" : "jpg",
|
||||
"filename" : str,
|
||||
"lang" : {"ja", "fa"},
|
||||
"manga" : "Gachiakuta",
|
||||
"manga_id" : "192aa767-2479-42c1-9780-8d65a2efd36a",
|
||||
"status" : "ongoing",
|
||||
"volume" : range(1, 16),
|
||||
"tags" : [
|
||||
"Monsters",
|
||||
"Action",
|
||||
"Comedy",
|
||||
"Survival",
|
||||
"Drama",
|
||||
"Fantasy",
|
||||
"Delinquents",
|
||||
"Supernatural",
|
||||
"Tragedy",
|
||||
],
|
||||
},
|
||||
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user