diff --git a/docs/configuration.rst b/docs/configuration.rst
index 399e0b6b..ecafd10f 100644
--- a/docs/configuration.rst
+++ b/docs/configuration.rst
@@ -640,7 +640,6 @@ Description
* ``simpcity``
* ``subscribestar``
* ``tapas``
- * ``tsumino``
* ``vipergirls``
* ``zerochan``
diff --git a/docs/gallery-dl.conf b/docs/gallery-dl.conf
index c3e8d7a0..cecc66ac 100644
--- a/docs/gallery-dl.conf
+++ b/docs/gallery-dl.conf
@@ -842,11 +842,6 @@
"include": ["avatar", "posts"]
}
},
- "tsumino":
- {
- "username": "",
- "password": ""
- },
"tumblr":
{
"access-token" : null,
diff --git a/docs/supportedsites.md b/docs/supportedsites.md
index eb8ee2e4..e6caa176 100644
--- a/docs/supportedsites.md
+++ b/docs/supportedsites.md
@@ -1123,12 +1123,6 @@ Consider all listed sites to potentially be NSFW.
Art, individual Images |
|
-
- | Tsumino |
- https://www.tsumino.com/ |
- Galleries, Search Results |
- Supported |
-
| Tumblr |
https://www.tumblr.com/ |
diff --git a/gallery_dl/extractor/__init__.py b/gallery_dl/extractor/__init__.py
index a179513e..2989bc36 100644
--- a/gallery_dl/extractor/__init__.py
+++ b/gallery_dl/extractor/__init__.py
@@ -213,7 +213,6 @@ modules = [
"tiktok",
"tmohentai",
"toyhouse",
- "tsumino",
"tumblr",
"tumblrgallery",
"tungsten",
diff --git a/gallery_dl/extractor/tsumino.py b/gallery_dl/extractor/tsumino.py
deleted file mode 100644
index 9ccfd6d1..00000000
--- a/gallery_dl/extractor/tsumino.py
+++ /dev/null
@@ -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">', '')),
- "date" : self.parse_datetime(
- extr('id="Uploaded">', '').strip(), "%Y %B %d"),
- "rating" : text.parse_float(extr(
- 'id="Rating">', '').partition(" ")[0]),
- "type" : text.remove_html(extr('id="Category">' , '')),
- "collection": text.remove_html(extr('id="Collection">', '')),
- "group" : text.split_html(extr('id="Group">' , '')),
- "artist" : text.split_html(extr('id="Artist">' , '')),
- "parody" : text.split_html(extr('id="Parody">' , '')),
- "characters": text.split_html(extr('id="Character">' , '')),
- "tags" : text.split_html(extr('id="Tag">' , '')),
- "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 '?=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",
- }
diff --git a/test/results/tsumino.py b/test/results/tsumino.py
deleted file mode 100644
index cbe2c77a..00000000
--- a/test/results/tsumino.py
+++ /dev/null
@@ -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",
-},
-
-)