diff --git a/docs/supportedsites.md b/docs/supportedsites.md
index 411a86de..5a627ebd 100644
--- a/docs/supportedsites.md
+++ b/docs/supportedsites.md
@@ -268,7 +268,7 @@ Consider all listed sites to potentially be NSFW.
| Discord |
https://discord.com/ |
- Channels, DMs, Messages, Servers |
+ Channels, DMs, Messages, Servers, Server Assets |
|
diff --git a/gallery_dl/extractor/discord.py b/gallery_dl/extractor/discord.py
index 54cc433e..8e0211b4 100644
--- a/gallery_dl/extractor/discord.py
+++ b/gallery_dl/extractor/discord.py
@@ -9,7 +9,6 @@
from .common import Extractor, Message
from .. import text, exception
-
BASE_PATTERN = r"(?:https?://)?discord\.com"
@@ -203,29 +202,47 @@ class DiscordExtractor(Extractor):
def parse_server(self, server):
self.server_metadata = {
- "server": server["name"],
+ "server" : server["name"],
"server_id": server["id"],
- "server_files": [],
- "owner_id": server["owner_id"]
+ "owner_id" : server["owner_id"],
+ "server_files": self.collect_server_assets_general(server),
}
- for icon_type, icon_path in (
- ("icon", "icons"),
- ("banner", "banners"),
- ("splash", "splashes"),
- ("discovery_splash", "discovery-splashes")
- ):
- if server.get(icon_type):
- self.server_metadata["server_files"].append({
- "url": (f"https://cdn.discordapp.com/{icon_path}/"
- f"{self.server_metadata['server_id']}/"
- f"{server[icon_type]}.png?size=4096"),
- "filename": icon_type,
- "extension": "png",
- })
-
return self.server_metadata
+ def collect_server_assets_general(self, server):
+ return [
+ {
+ "url": (f"https://cdn.discordapp.com/{asset_path}/"
+ f"{server['id']}/{asset_id}.png?size=4096"),
+ "id" : f"{server['id']}/{asset_id}",
+ "label" : "general",
+ "name" : asset_type,
+ "filename" : asset_type,
+ "extension": "png",
+ }
+ for asset_type, asset_path in (
+ ("icon" , "icons"),
+ ("banner", "banners"),
+ ("splash", "splashes"),
+ ("discovery_splash", "discovery-splashes")
+ )
+ if (asset_id := server.get(asset_type))
+ ]
+
+ def collect_server_assets_type(self, server, asset_type):
+ return [
+ {
+ **asset,
+ "url": (f"https://cdn.discordapp.com/{asset_type}/"
+ f"{asset['id']}.png?size=4096"),
+ "label" : asset_type,
+ "filename" : f"{asset['name']} ({asset['id']})",
+ "extension": "png",
+ }
+ for asset in assets
+ ] if (assets := server.get(asset_type)) else ()
+
def build_server_and_channels(self, server_id):
self.parse_server(self.api.get_server(server_id))
@@ -266,6 +283,38 @@ class DiscordMessageExtractor(DiscordExtractor):
self.api.get_message(channel_id, message_id))
+class DiscordServerAssetsExtractor(DiscordExtractor):
+ subcategory = "server-assets"
+ filename_fmt = "{name} ({id}).{extension}"
+ directory_fmt = ["{category}", "{server_id}_{server}", "Assets"]
+ archive_fmt = "asset_{server_id}_{id}"
+ pattern = (BASE_PATTERN +
+ r"/channels/(\d+)/(?:assets?|files)(?:/([\w-]+))?/?$")
+ example = "https://discord.com/channels/1234567890/assets"
+
+ def items(self):
+ server_id, asset_type = self.groups
+ server = self.api.get_server(server_id)
+ parsed = self.parse_server(server)
+
+ if asset_type is None:
+ assets = [
+ *self.collect_server_assets_general(server),
+ *self.collect_server_assets_type(server, "emojis"),
+ *self.collect_server_assets_type(server, "stickers"),
+ ]
+ elif asset_type == "general":
+ assets = self.collect_server_assets_general(server)
+ else:
+ assets = self.collect_server_assets_type(server, asset_type)
+
+ parsed["count"] = len(assets)
+ yield Message.Directory, "", parsed
+ for asset in assets:
+ asset.update(parsed)
+ yield Message.Url, asset["url"], asset
+
+
class DiscordServerExtractor(DiscordExtractor):
subcategory = "server"
pattern = BASE_PATTERN + r"/channels/(\d+)/?$"
diff --git a/test/results/discord.py b/test/results/discord.py
index 5b2350db..f3cbd1aa 100644
--- a/test/results/discord.py
+++ b/test/results/discord.py
@@ -73,4 +73,38 @@ __tests__ = (
"#class" : discord.DiscordDirectMessagesExtractor,
},
+{
+ "#url" : "https://discord.com/channels/403905762268545024/assets",
+ "#class" : discord.DiscordServerAssetsExtractor,
+ "#auth" : "token",
+ "#count" : range(380, 450),
+
+ "name" : str,
+ "filename" : str,
+ "extension": "png",
+ "id" : str,
+ "label" : {"general", "emojis", "stickers"},
+ "owner_id" : "699203962691256400",
+ "server" : "MangaDex",
+ "server_id": "403905762268545024",
+ "url" : str,
+},
+
+{
+ "#url" : "https://discord.com/channels/403905762268545024/assets/general",
+ "#class" : discord.DiscordServerAssetsExtractor,
+ "#auth" : "token",
+ "#count" : 3,
+
+ "name" : {"icon", "banner", "splash"},
+ "filename" : {"icon", "banner", "splash"},
+ "extension": "png",
+ "id" : str,
+ "label" : "general",
+ "owner_id" : "699203962691256400",
+ "server" : "MangaDex",
+ "server_id": "403905762268545024",
+ "url" : str,
+},
+
)