diff --git a/docs/supportedsites.md b/docs/supportedsites.md
index 1e945926..363aa0ac 100644
--- a/docs/supportedsites.md
+++ b/docs/supportedsites.md
@@ -157,6 +157,12 @@ Consider all listed sites to potentially be NSFW.
Tag Searches |
|
+
+ Comicfury |
+ https://comicfury.com |
+ Comic Issues, Comics |
+ |
+
Coomer |
https://coomer.su/ |
diff --git a/gallery_dl/extractor/__init__.py b/gallery_dl/extractor/__init__.py
index e103cb1b..478c9e0c 100644
--- a/gallery_dl/extractor/__init__.py
+++ b/gallery_dl/extractor/__init__.py
@@ -35,6 +35,7 @@ modules = [
"catbox",
"chevereto",
"cien",
+ "comicfury",
"comicvine",
"cyberdrop",
"danbooru",
diff --git a/gallery_dl/extractor/comicfury.py b/gallery_dl/extractor/comicfury.py
new file mode 100644
index 00000000..f3df95f0
--- /dev/null
+++ b/gallery_dl/extractor/comicfury.py
@@ -0,0 +1,147 @@
+# -*- 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.
+
+"""Extractors for https://comicfury.com"""
+
+import re
+import itertools
+from .common import Extractor, Message
+from .. import text
+
+
+CF_DOMAINS = (
+ r"([\w-]+)\.(?:thecomicseries\.com|the-comic\.org"
+ r"|thecomicstrip\.org|webcomic\.ws|cfw\.me)"
+)
+
+
+class ComicfuryExtractor(Extractor):
+ """Base class for ComicFury extractors"""
+ category = "comicfury"
+ directory_fmt = ("{category}", "{comic}")
+ filename_fmt = "{category}_{comic}_{id}_{num:>02}.{extension}"
+ archive_fmt = "{filename}"
+ root = "https://comicfury.com"
+ cookies_domain = "comicfury.com"
+
+ def _init(self):
+ self._search_segments = re.compile(
+ (r'\n *\n'
+ r'([\s\S]+?)\n *
\n')).search
+
+ def request(self, url, **kwargs):
+ resp = Extractor.request(self, url, **kwargs)
+ if 'Content Warning
' in resp.text:
+ token = self.session.cookies.get(
+ "token", domain=self.cookies_domain)
+ resp = Extractor.request(self, url, method="POST", data={
+ "proceed": "View Webcomic",
+ "token": token,
+ }, **kwargs)
+ return resp
+
+ def _parse_page(self, page):
+ comic_name, pos = text.extract(
+ page, '', '
')
+ relative_id, pos = text.extract(
+ page, 'Comic #', ':', pos)
+ comic, pos = text.extract(
+ page, '', '', pos)
+
+ segments = self._search_segments(page, pos)
+ pos = segments.end(0)
+ urls = list(text.extract_iter(
+ segments.group(1), '
', '')
+ new_url = text.extr(
+ div, '')
+ if not new_url:
+ break
+ url = text.urljoin(url, text.unescape(new_url))
diff --git a/test/results/comicfury.py b/test/results/comicfury.py
new file mode 100644
index 00000000..36d029df
--- /dev/null
+++ b/test/results/comicfury.py
@@ -0,0 +1,102 @@
+# -*- 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 comicfury
+
+
+__tests__ = (
+{
+ "#url" : "https://rain.thecomicseries.com/comics/pl/73003",
+ "#category": ("", "comicfury", "issue"),
+ "#class" : comicfury.ComicfuryIssueExtractor,
+ "#count" : 1,
+ "#urls" : "https://img.comicfury.com/comics/c8f813e19a0aae0f2a0b57a6b36ceec52058036413.png",
+
+ "comic_name" : "Rain",
+ "comic" : "rain",
+ "relative_id" : 6,
+ "id" : 73003,
+ "chapter_id" : 2770,
+ "chapter_name": "Ch 1: The New Girl",
+ "title" : "Chapter 1 - The New Girl",
+},
+
+{
+ "#url" : "https://grinders.the-comic.org/comics/first",
+ "#category": ("", "comicfury", "issue"),
+ "#class" : comicfury.ComicfuryIssueExtractor,
+ "#count" : 1,
+ "#urls" : "https://img.comicfury.com/comics/184/43571a1579840219f1635377961.png",
+
+ "comic_name" : "Grinder$",
+ "comic" : "grinders",
+ "relative_id" : 1,
+ "id" : 1137093,
+ "chapter_id" : 48527,
+ "chapter_name": "Foam",
+ "title" : "Teaser",
+},
+
+{
+ "#url" : "https://belovedchainscomic.thecomicstrip.org/comics/1",
+ "#category": ("", "comicfury", "issue"),
+ "#class" : comicfury.ComicfuryIssueExtractor,
+},
+
+{
+ "#url" : "https://belovedchainscomic.webcomic.ws/comics/",
+ "#category": ("", "comicfury", "issue"),
+ "#class" : comicfury.ComicfuryIssueExtractor,
+},
+
+{
+ "#url" : "https://comicfury.com/read/MKsJekyllAndHyde/comic/last",
+ "#category": ("", "comicfury", "issue"),
+ "#class" : comicfury.ComicfuryIssueExtractor,
+ "#count" : 1,
+ "#urls" : "https://img.comicfury.com/comics/222/37111a1634996413b60163f1077624721.png",
+
+ "comic_name" : "MK's The Strange Case of Dr. Jekyll and Mr. Hyde",
+ "comic" : "MKsJekyllAndHyde",
+ "relative_id" : 622,
+ "id" : 1493321,
+ "chapter_id" : 57040,
+ "chapter_name": "Epilogue 3",
+ "title" : "THE END",
+},
+
+{
+ "#url" : "https://comicfury.com/read/rain-tradfr",
+ "#category": ("", "comicfury", "issue"),
+ "#class" : comicfury.ComicfuryIssueExtractor,
+ "#count" : 1,
+ "#urls" : "https://img.comicfury.com/comics/218/49338a1624179795b80143f379314885.jpg",
+
+ "comic_name" : "Rain, la traduction française",
+ "comic" : "rain-tradfr",
+ "relative_id" : 1,
+ "id" : 1381699,
+ "chapter_id" : 56171,
+ "chapter_name": "Hors Chapitre",
+ "title" : "RAIN",
+},
+
+{
+ "#url" : "https://comicfury.com/comicprofile.php?url=lanternsofarcadia",
+ "#category": ("", "comicfury", "comic"),
+ "#class" : comicfury.ComicfuryComicExtractor,
+ "#range" : "1-6",
+ "#sha1_url" : "d4080dcb41f5c019e1ceb450a624041208ccdcb8",
+ "#sha1_content": "0c1937e4d177ce55afbfe30ab9376700c6cf619f",
+},
+
+{
+ "#url" : "https://bloomer-layout.cfw.me",
+ "#category": ("", "comicfury", "comic"),
+ "#class" : comicfury.ComicfuryComicExtractor,
+},
+
+)