[tsumino] remove module
" Tsumino - The End We're shutting Tsumino down. "
This commit is contained in:
@@ -640,7 +640,6 @@ Description
|
|||||||
* ``simpcity``
|
* ``simpcity``
|
||||||
* ``subscribestar``
|
* ``subscribestar``
|
||||||
* ``tapas``
|
* ``tapas``
|
||||||
* ``tsumino``
|
|
||||||
* ``vipergirls``
|
* ``vipergirls``
|
||||||
* ``zerochan``
|
* ``zerochan``
|
||||||
|
|
||||||
|
|||||||
@@ -842,11 +842,6 @@
|
|||||||
"include": ["avatar", "posts"]
|
"include": ["avatar", "posts"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tsumino":
|
|
||||||
{
|
|
||||||
"username": "",
|
|
||||||
"password": ""
|
|
||||||
},
|
|
||||||
"tumblr":
|
"tumblr":
|
||||||
{
|
{
|
||||||
"access-token" : null,
|
"access-token" : null,
|
||||||
|
|||||||
@@ -1123,12 +1123,6 @@ Consider all listed sites to potentially be NSFW.
|
|||||||
<td>Art, individual Images</td>
|
<td>Art, individual Images</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="tsumino" title="tsumino">
|
|
||||||
<td>Tsumino</td>
|
|
||||||
<td>https://www.tsumino.com/</td>
|
|
||||||
<td>Galleries, Search Results</td>
|
|
||||||
<td>Supported</td>
|
|
||||||
</tr>
|
|
||||||
<tr id="tumblr" title="tumblr">
|
<tr id="tumblr" title="tumblr">
|
||||||
<td>Tumblr</td>
|
<td>Tumblr</td>
|
||||||
<td>https://www.tumblr.com/</td>
|
<td>https://www.tumblr.com/</td>
|
||||||
|
|||||||
@@ -213,7 +213,6 @@ modules = [
|
|||||||
"tiktok",
|
"tiktok",
|
||||||
"tmohentai",
|
"tmohentai",
|
||||||
"toyhouse",
|
"toyhouse",
|
||||||
"tsumino",
|
|
||||||
"tumblr",
|
"tumblr",
|
||||||
"tumblrgallery",
|
"tumblrgallery",
|
||||||
"tungsten",
|
"tungsten",
|
||||||
|
|||||||
@@ -1,179 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright 2019-2026 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://www.tsumino.com/"""
|
|
||||||
|
|
||||||
from .common import GalleryExtractor, Extractor, Message
|
|
||||||
from .. import text, exception
|
|
||||||
from ..cache import cache
|
|
||||||
|
|
||||||
|
|
||||||
class TsuminoBase():
|
|
||||||
"""Base class for tsumino extractors"""
|
|
||||||
category = "tsumino"
|
|
||||||
cookies_domain = "www.tsumino.com"
|
|
||||||
root = "https://www.tsumino.com"
|
|
||||||
|
|
||||||
def login(self):
|
|
||||||
username, password = self._get_auth_info()
|
|
||||||
if username:
|
|
||||||
self.cookies_update(self._login_impl(username, password))
|
|
||||||
else:
|
|
||||||
self.cookies.setdefault(
|
|
||||||
"ASP.NET_SessionId", "x1drgggilez4cpkttneukrc5")
|
|
||||||
|
|
||||||
@cache(maxage=14*86400, keyarg=1)
|
|
||||||
def _login_impl(self, username, password):
|
|
||||||
self.log.info("Logging in as %s", username)
|
|
||||||
url = self.root + "/Account/Login"
|
|
||||||
headers = {"Referer": url}
|
|
||||||
data = {"Username": username, "Password": password}
|
|
||||||
|
|
||||||
response = self.request(url, method="POST", headers=headers, data=data)
|
|
||||||
if not response.history:
|
|
||||||
raise exception.AuthenticationError()
|
|
||||||
return self.cookies
|
|
||||||
|
|
||||||
|
|
||||||
class TsuminoGalleryExtractor(TsuminoBase, GalleryExtractor):
|
|
||||||
"""Extractor for image galleries on tsumino.com"""
|
|
||||||
pattern = (r"(?i)(?:https?://)?(?:www\.)?tsumino\.com"
|
|
||||||
r"/(?:entry|Book/Info|Read/(?:Index|View))/(\d+)")
|
|
||||||
example = "https://www.tsumino.com/entry/12345"
|
|
||||||
|
|
||||||
def __init__(self, match):
|
|
||||||
self.gallery_id = match[1]
|
|
||||||
url = f"{self.root}/entry/{self.gallery_id}"
|
|
||||||
GalleryExtractor.__init__(self, match, url)
|
|
||||||
|
|
||||||
def metadata(self, page):
|
|
||||||
extr = text.extract_from(page)
|
|
||||||
title = extr('"og:title" content="', '"')
|
|
||||||
title_en, _, title_jp = text.unescape(title).partition("/")
|
|
||||||
title_en = title_en.strip()
|
|
||||||
title_jp = title_jp.strip()
|
|
||||||
|
|
||||||
return {
|
|
||||||
"gallery_id": text.parse_int(self.gallery_id),
|
|
||||||
"title" : title_en or title_jp,
|
|
||||||
"title_en" : title_en,
|
|
||||||
"title_jp" : title_jp,
|
|
||||||
"thumbnail" : extr('"og:image" content="', '"'),
|
|
||||||
"uploader" : text.remove_html(extr('id="Uploader">', '</div>')),
|
|
||||||
"date" : self.parse_datetime(
|
|
||||||
extr('id="Uploaded">', '</div>').strip(), "%Y %B %d"),
|
|
||||||
"rating" : text.parse_float(extr(
|
|
||||||
'id="Rating">', '</div>').partition(" ")[0]),
|
|
||||||
"type" : text.remove_html(extr('id="Category">' , '</div>')),
|
|
||||||
"collection": text.remove_html(extr('id="Collection">', '</div>')),
|
|
||||||
"group" : text.split_html(extr('id="Group">' , '</div>')),
|
|
||||||
"artist" : text.split_html(extr('id="Artist">' , '</div>')),
|
|
||||||
"parody" : text.split_html(extr('id="Parody">' , '</div>')),
|
|
||||||
"characters": text.split_html(extr('id="Character">' , '</div>')),
|
|
||||||
"tags" : text.split_html(extr('id="Tag">' , '</div>')),
|
|
||||||
"language" : "English",
|
|
||||||
"lang" : "en",
|
|
||||||
}
|
|
||||||
|
|
||||||
def images(self, page):
|
|
||||||
url = f"{self.root}/Read/Index/{self.gallery_id}?page=1"
|
|
||||||
headers = {"Referer": self.page_url}
|
|
||||||
response = self.request(url, headers=headers, fatal=False)
|
|
||||||
|
|
||||||
if "/Auth/" in response.url:
|
|
||||||
raise exception.AbortExtraction(
|
|
||||||
f"Failed to get gallery JSON data. Visit '{response.url}' "
|
|
||||||
f"in a browser and solve the CAPTCHA to continue.")
|
|
||||||
|
|
||||||
page = response.text
|
|
||||||
tpl, pos = text.extract(page, 'data-cdn="', '"')
|
|
||||||
cnt, pos = text.extract(page, '> of ', '<', pos)
|
|
||||||
base, _, params = text.unescape(tpl).partition("[PAGE]")
|
|
||||||
|
|
||||||
return [
|
|
||||||
(base + str(i) + params, None)
|
|
||||||
for i in range(1, text.parse_int(cnt)+1)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class TsuminoSearchExtractor(TsuminoBase, Extractor):
|
|
||||||
"""Extractor for search results on tsumino.com"""
|
|
||||||
subcategory = "search"
|
|
||||||
pattern = r"(?i)(?:https?://)?(?:www\.)?tsumino\.com/(?:Books/?)?#(.+)"
|
|
||||||
example = "https://www.tsumino.com/Books#QUERY"
|
|
||||||
|
|
||||||
def __init__(self, match):
|
|
||||||
Extractor.__init__(self, match)
|
|
||||||
self.query = match[1]
|
|
||||||
|
|
||||||
def items(self):
|
|
||||||
for gallery in self.galleries():
|
|
||||||
url = f"{self.root}/entry/{gallery['id']}"
|
|
||||||
gallery["_extractor"] = TsuminoGalleryExtractor
|
|
||||||
yield Message.Queue, url, gallery
|
|
||||||
|
|
||||||
def galleries(self):
|
|
||||||
"""Return all gallery results matching 'self.query'"""
|
|
||||||
url = self.root + "/Search/Operate?type=Book"
|
|
||||||
headers = {
|
|
||||||
"Referer": self.root + "/",
|
|
||||||
"X-Requested-With": "XMLHttpRequest",
|
|
||||||
}
|
|
||||||
data = {
|
|
||||||
"PageNumber": 1,
|
|
||||||
"Text": "",
|
|
||||||
"Sort": "Newest",
|
|
||||||
"List": "0",
|
|
||||||
"Length": "0",
|
|
||||||
"MinimumRating": "0",
|
|
||||||
"ExcludeList": "0",
|
|
||||||
"CompletelyExcludeHated": "false",
|
|
||||||
}
|
|
||||||
data.update(self._parse(self.query))
|
|
||||||
|
|
||||||
while True:
|
|
||||||
info = self.request_json(
|
|
||||||
url, method="POST", headers=headers, data=data)
|
|
||||||
|
|
||||||
for gallery in info["data"]:
|
|
||||||
yield gallery["entry"]
|
|
||||||
|
|
||||||
if info["pageNumber"] >= info["pageCount"]:
|
|
||||||
return
|
|
||||||
data["PageNumber"] += 1
|
|
||||||
|
|
||||||
def _parse(self, query):
|
|
||||||
if not query:
|
|
||||||
return {}
|
|
||||||
try:
|
|
||||||
if query[0] == "?":
|
|
||||||
return self._parse_simple(query)
|
|
||||||
return self.utils("/jsurl").parse(query)
|
|
||||||
except Exception as exc:
|
|
||||||
raise exception.AbortExtraction(
|
|
||||||
f"Invalid search query '{query}' ({exc})")
|
|
||||||
|
|
||||||
def _parse_simple(self, query):
|
|
||||||
"""Parse search query with format '?<key>=value>'"""
|
|
||||||
key, _, value = query.partition("=")
|
|
||||||
tag_types = {
|
|
||||||
"Tag": "1",
|
|
||||||
"Category": "2",
|
|
||||||
"Collection": "3",
|
|
||||||
"Group": "4",
|
|
||||||
"Artist": "5",
|
|
||||||
"Parody": "6",
|
|
||||||
"Character": "7",
|
|
||||||
"Uploader": "100",
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
"Tags[0][Type]": tag_types[key[1:].capitalize()],
|
|
||||||
"Tags[0][Text]": text.unquote(value).replace("+", " "),
|
|
||||||
"Tags[0][Exclude]": "false",
|
|
||||||
}
|
|
||||||
@@ -1,72 +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 tsumino
|
|
||||||
|
|
||||||
|
|
||||||
__tests__ = (
|
|
||||||
{
|
|
||||||
"#url" : "https://www.tsumino.com/entry/40996",
|
|
||||||
"#category": ("", "tsumino", "gallery"),
|
|
||||||
"#class" : tsumino.TsuminoGalleryExtractor,
|
|
||||||
"#pattern" : r"https://content.tsumino.com/parts/40996/\d+\?key=\w+",
|
|
||||||
|
|
||||||
"title" : r"re:Shikoshiko Daisuki Nightingale \+ Kaijou",
|
|
||||||
"title_en" : r"re:Shikoshiko Daisuki Nightingale \+ Kaijou",
|
|
||||||
"title_jp" : "シコシコ大好きナイチンゲール + 会場限定おまけ本",
|
|
||||||
"gallery_id": 40996,
|
|
||||||
"date" : "dt:2018-06-29 00:00:00",
|
|
||||||
"count" : 42,
|
|
||||||
"collection": "",
|
|
||||||
"artist" : ["Itou Life"],
|
|
||||||
"group" : ["Itou Life"],
|
|
||||||
"parody" : list,
|
|
||||||
"characters": list,
|
|
||||||
"tags" : list,
|
|
||||||
"type" : "Doujinshi",
|
|
||||||
"rating" : float,
|
|
||||||
"uploader" : "sehki",
|
|
||||||
"lang" : "en",
|
|
||||||
"language" : "English",
|
|
||||||
"thumbnail" : "https://content.tsumino.com/thumbs/40996/1",
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"#url" : "https://www.tsumino.com/Book/Info/40996",
|
|
||||||
"#category": ("", "tsumino", "gallery"),
|
|
||||||
"#class" : tsumino.TsuminoGalleryExtractor,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"#url" : "https://www.tsumino.com/Read/View/45834",
|
|
||||||
"#category": ("", "tsumino", "gallery"),
|
|
||||||
"#class" : tsumino.TsuminoGalleryExtractor,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"#url" : "https://www.tsumino.com/Read/Index/45834",
|
|
||||||
"#category": ("", "tsumino", "gallery"),
|
|
||||||
"#class" : tsumino.TsuminoGalleryExtractor,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"#url" : "https://www.tsumino.com/Books#?Character=Reimu+Hakurei",
|
|
||||||
"#category": ("", "tsumino", "search"),
|
|
||||||
"#class" : tsumino.TsuminoSearchExtractor,
|
|
||||||
"#pattern" : tsumino.TsuminoGalleryExtractor.pattern,
|
|
||||||
"#range" : "1-40",
|
|
||||||
"#count" : 40,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"#url" : "http://www.tsumino.com/Books#~(Tags~(~(Type~7~Text~'Reimu*20Hakurei~Exclude~false)~(Type~'1~Text~'Pantyhose~Exclude~false)))#",
|
|
||||||
"#category": ("", "tsumino", "search"),
|
|
||||||
"#class" : tsumino.TsuminoSearchExtractor,
|
|
||||||
"#pattern" : tsumino.TsuminoGalleryExtractor.pattern,
|
|
||||||
"#count" : ">= 3",
|
|
||||||
},
|
|
||||||
|
|
||||||
)
|
|
||||||
Reference in New Issue
Block a user