diff --git a/docs/supportedsites.md b/docs/supportedsites.md
index cc4b74ed..57d313e4 100644
--- a/docs/supportedsites.md
+++ b/docs/supportedsites.md
@@ -190,7 +190,7 @@ Consider all listed sites to potentially be NSFW.
| Coomer |
https://coomer.su/ |
- Favorites, Posts, User Profiles |
+ Artists, Favorites, Posts, User Profiles |
Supported |
@@ -496,7 +496,7 @@ Consider all listed sites to potentially be NSFW.
| Kemono |
https://kemono.su/ |
- Discord Servers, Favorites, Posts, User Profiles |
+ Artists, Discord Servers, Favorites, Posts, User Profiles |
Supported |
diff --git a/gallery_dl/extractor/kemonoparty.py b/gallery_dl/extractor/kemonoparty.py
index 2e85553b..9b2e8e1b 100644
--- a/gallery_dl/extractor/kemonoparty.py
+++ b/gallery_dl/extractor/kemonoparty.py
@@ -528,6 +528,44 @@ class KemonopartyFavoriteExtractor(KemonopartyExtractor):
yield Message.Queue, url, post
+class KemonopartyArtistsExtractor(KemonopartyExtractor):
+ """Extractor for kemono artists"""
+ subcategory = "artists"
+ pattern = BASE_PATTERN + r"/artists(?:\?([^#]+))?"
+ example = "https://kemono.su/artists"
+
+ def items(self):
+ params = text.parse_query(self.groups[2])
+ users = self.api.creators()
+
+ if params.get("service"):
+ service = params["service"].lower()
+ users = [user for user in users
+ if user["service"] == service]
+
+ if params.get("q"):
+ q = params["q"].lower()
+ users = [user for user in users
+ if q in user["name"].lower()]
+
+ sort = params.get("sort_by") or "favorited"
+ order = params.get("order") or "desc"
+ users.sort(key=lambda user: user[sort] or util.NONE,
+ reverse=(order != "asc"))
+
+ for user in users:
+ service = user["service"]
+ if service == "discord":
+ user["_extractor"] = KemonopartyDiscordServerExtractor
+ url = "{}/discord/server/{}".format(
+ self.root, user["id"])
+ else:
+ user["_extractor"] = KemonopartyUserExtractor
+ url = "{}/{}/user/{}".format(
+ self.root, service, user["id"])
+ yield Message.Queue, url, user
+
+
class KemonoAPI():
"""Interface for the Kemono API v1.1.0
@@ -547,6 +585,10 @@ class KemonoAPI():
endpoint = "/file/" + file_hash
return self._call(endpoint)
+ def creators(self):
+ endpoint = "/creators.txt"
+ return self._call(endpoint)
+
def creator_posts(self, service, creator_id,
offset=0, query=None, tags=None):
endpoint = "/{}/user/{}".format(service, creator_id)
diff --git a/test/results/kemonoparty.py b/test/results/kemonoparty.py
index 93ce4ed7..86293600 100644
--- a/test/results/kemonoparty.py
+++ b/test/results/kemonoparty.py
@@ -680,4 +680,50 @@ __tests__ = (
"#class" : kemonoparty.KemonopartyFavoriteExtractor,
},
+{
+ "#url" : "https://kemono.su/artists?q=aMSa",
+ "#category": ("", "kemonoparty", "artists"),
+ "#class" : kemonoparty.KemonopartyArtistsExtractor,
+ "#pattern" : kemonoparty.KemonopartyUserExtractor.pattern,
+ "#urls" : (
+ "https://kemono.su/patreon/user/91205314",
+ "https://kemono.su/patreon/user/51528107",
+ "https://kemono.su/fanbox/user/12812028",
+ "https://kemono.su/patreon/user/35237747",
+ "https://kemono.su/patreon/user/8296916",
+ "https://kemono.su/patreon/user/155095324",
+ "https://kemono.su/patreon/user/75988930",
+ "https://kemono.su/patreon/user/93703989",
+ "https://kemono.su/patreon/user/100292687",
+ "https://kemono.su/patreon/user/138609443",
+ "https://kemono.su/patreon/user/61646879",
+ "https://kemono.su/patreon/user/110669843",
+ "https://kemono.su/patreon/user/44343773",
+ "https://kemono.su/patreon/user/77920059",
+ "https://kemono.su/patreon/user/102386631",
+ ),
+
+ "favorited": int,
+ "id" : str,
+ "indexed" : int,
+ "name" : str,
+ "service" : {"patreon", "fanbox"},
+ "updated" : int,
+},
+
+{
+ "#url" : "https://kemono.su/artists?q=Axe&service=discord&sort_by=name&order=asc",
+ "#category": ("", "kemonoparty", "artists"),
+ "#class" : kemonoparty.KemonopartyArtistsExtractor,
+ "#pattern" : kemonoparty.KemonopartyDiscordServerExtractor.pattern,
+ "#urls" : "https://kemono.su/discord/server/1168450323023663164",
+
+ "favorited": range(40, 80),
+ "id" : "1168450323023663164",
+ "indexed" : 1710201675,
+ "name" : "Axel Colored Workshop",
+ "service" : "discord",
+ "updated" : range(1740000000, 2000000000),
+},
+
)