From 44e18f9b2f7d4b1fec9e2fbf592040b8c1c28303 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20F=C3=A4hrmann?= Date: Sun, 1 Feb 2026 22:15:06 +0100 Subject: [PATCH] [tsumino] remove module " Tsumino - The End We're shutting Tsumino down. " --- docs/configuration.rst | 1 - docs/gallery-dl.conf | 5 - docs/supportedsites.md | 6 -- gallery_dl/extractor/__init__.py | 1 - gallery_dl/extractor/tsumino.py | 179 ------------------------------- test/results/tsumino.py | 72 ------------- 6 files changed, 264 deletions(-) delete mode 100644 gallery_dl/extractor/tsumino.py delete mode 100644 test/results/tsumino.py 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", -}, - -)