diff --git a/docs/configuration.rst b/docs/configuration.rst
index 88452f12..fa278861 100644
--- a/docs/configuration.rst
+++ b/docs/configuration.rst
@@ -427,6 +427,7 @@ Default
``[Danbooru]``,
``[E621]``,
``[foolfuuka]:search``,
+ ``hdoujin``,
``itaku``,
``newgrounds``,
``[philomena]``,
@@ -438,6 +439,7 @@ Default
``scrolller``,
``sizebooru``,
``soundgasm``,
+ ``thehentaiworld``,
``urlgalleries``,
``vk``,
``webtoons``,
diff --git a/docs/gallery-dl.conf b/docs/gallery-dl.conf
index 173b3119..e4cba423 100644
--- a/docs/gallery-dl.conf
+++ b/docs/gallery-dl.conf
@@ -769,6 +769,10 @@
{
"format": ["gif", "mp4", "webm", "webp"]
},
+ "thehentaiworld":
+ {
+ "sleep-request": "0.5-1.5"
+ },
"tiktok":
{
"audio" : true,
diff --git a/docs/supportedsites.md b/docs/supportedsites.md
index 727f4124..1eeb2a33 100644
--- a/docs/supportedsites.md
+++ b/docs/supportedsites.md
@@ -997,6 +997,12 @@ Consider all listed sites to potentially be NSFW.
individual Images, Search Results, User Profiles |
|
+
+ | The Hentai World |
+ https://thehentaiworld.com/ |
+ Posts, Tag Searches |
+ |
+
| TikTok |
https://www.tiktok.com/ |
diff --git a/gallery_dl/extractor/__init__.py b/gallery_dl/extractor/__init__.py
index 83cebc1f..abdb6cc8 100644
--- a/gallery_dl/extractor/__init__.py
+++ b/gallery_dl/extractor/__init__.py
@@ -191,6 +191,7 @@ modules = [
"tcbscans",
"telegraph",
"tenor",
+ "thehentaiworld",
"tiktok",
"tmohentai",
"toyhouse",
diff --git a/gallery_dl/extractor/thehentaiworld.py b/gallery_dl/extractor/thehentaiworld.py
new file mode 100644
index 00000000..c366f9c4
--- /dev/null
+++ b/gallery_dl/extractor/thehentaiworld.py
@@ -0,0 +1,132 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2025 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://thehentaiworld.com/"""
+
+from .common import Extractor, Message
+from .. import text, util
+import collections
+
+BASE_PATTERN = r"(?:https?://)?(?:www\.)?thehentaiworld\.com"
+
+
+class ThehentaiworldExtractor(Extractor):
+ """Base class for thehentaiworld extractors"""
+ category = "thehentaiworld"
+ root = "https://thehentaiworld.com"
+ filename_fmt = "{title} ({id}{num:?-//}).{extension}"
+ archive_fmt = "{id}_{num}"
+ request_interval = (0.5, 1.5)
+
+ def items(self):
+ for url in self.posts():
+ post = self._extract_post(url)
+
+ if "file_urls" in post:
+ urls = post["file_urls"]
+ post["count"] = len(urls)
+ yield Message.Directory, post
+ for post["num"], url in enumerate(urls, 1):
+ text.nameext_from_url(url, post)
+ yield Message.Url, url, post
+ else:
+ yield Message.Directory, post
+ url = post["file_url"]
+ text.nameext_from_url(url, post)
+ yield Message.Url, url, post
+
+ def _extract_post(self, url):
+ extr = text.extract_from(self.request(url).text)
+
+ post = {
+ "num" : 0,
+ "count" : 1,
+ "title" : text.unescape(extr("", "<").strip()),
+ "id" : text.parse_int(extr(" postid-", " ")),
+ "slug" : extr(" post-", '"'),
+ "tags" : extr('id="tagsHead">', ""),
+ "date" : text.parse_datetime(extr(
+ "Posted: ", "<"), "%Y-%m-%d"),
+ }
+
+ if "/videos/" in url:
+ post["type"] = "video"
+ post["width"] = post["height"] = 0
+ post["votes"] = text.parse_int(extr("(", ""))
+ post["score"] = text.parse_float(extr("", "<"))
+ post["file_url"] = extr('Size: ", " "))
+ post["height"] = text.parse_int(extr("x ", "<"))
+ post["file_url"] = extr('a href="', '"')
+ post["votes"] = text.parse_int(extr("(", ""))
+ post["score"] = text.parse_float(extr("", "<"))
+
+ if doujin := extr('<"):
+ repl = text.re(r"-220x\d+\.").sub
+ post["file_urls"] = [
+ repl(".", url)
+ for url in text.extract_iter(
+ doujin, 'class="border" src="', '"')
+ ]
+
+ tags = collections.defaultdict(list)
+ pattern = text.re(r'([^<]+)')
+ for tag_type, tag_name in pattern.findall(post["tags"]):
+ tags[tag_type].append(tag_name)
+ post["tags"] = tags_list = []
+ for key, value in tags.items():
+ tags_list.extend(value)
+ post[f"tags_{key}" if key else "tags_general"] = value
+
+ return post
+
+ def _pagination(self, endpoint):
+ base = f"{self.root}{endpoint}"
+ pnum = self.page_start
+
+ while True:
+ url = base if pnum < 2 else f"{base}page/{pnum}/"
+ page = self.request(url).text
+
+ yield from text.extract_iter(text.extr(
+ page, 'id="thumbContainer"', "