diff --git a/docs/supportedsites.md b/docs/supportedsites.md
index 1a4d5959..df533243 100644
--- a/docs/supportedsites.md
+++ b/docs/supportedsites.md
@@ -940,7 +940,7 @@ Consider all listed sites to potentially be NSFW.
| Tenor |
https://tenor.com/ |
- individual Images, Search Results |
+ individual Images, Search Results, User Profiles |
|
diff --git a/gallery_dl/extractor/tenor.py b/gallery_dl/extractor/tenor.py
index 3ea15e8f..42787a7c 100644
--- a/gallery_dl/extractor/tenor.py
+++ b/gallery_dl/extractor/tenor.py
@@ -57,6 +57,47 @@ class TenorExtractor(Extractor):
if fmt in media_formats:
return media_formats[fmt]
+ def _search_results(self, query):
+ url = "https://tenor.googleapis.com/v2/search"
+ params = {
+ "appversion": "browser-r20250225-1",
+ "prettyPrint": "false",
+ "key": "AIzaSyC-P6_qz3FzCoXGLk6tgitZo4jEJ5mLzD8",
+ "client_key": "tenor_web",
+ "locale": "en",
+ "anon_id": "",
+ "q": query,
+ "limit": "50",
+ "contentfilter": "low",
+ "media_filter": "gif,gif_transparent,mediumgif,tinygif,"
+ "tinygif_transparent,webp,webp_transparent,"
+ "tinywebp,tinywebp_transparent,tinymp4,mp4,webm,"
+ "originalgif,gifpreview",
+ "fields": "next,results.id,results.media_formats,results.title,"
+ "results.h1_title,results.long_title,results.itemurl,"
+ "results.url,results.created,results.user,"
+ "results.shares,results.embed,results.hasaudio,"
+ "results.policy_status,results.source_id,results.flags,"
+ "results.tags,results.content_rating,results.bg_color,"
+ "results.legacy_info,results.geographic_restriction,"
+ "results.content_description",
+ "pos": None,
+ "component": "web_desktop",
+ }
+ headers = {
+ "Referer": self.root + "/",
+ "Origin" : self.root,
+ }
+
+ while True:
+ data = self.request(url, params=params, headers=headers).json()
+
+ yield from data["results"]
+
+ params["pos"] = data.get("next")
+ if not params["pos"]:
+ return
+
def metadata(self):
return False
@@ -93,43 +134,14 @@ class TenorSearchExtractor(TenorExtractor):
return {"search_tags": self.search_tags}
def gifs(self):
- url = "https://tenor.googleapis.com/v2/search"
- params = {
- "appversion": "browser-r20250225-1",
- "prettyPrint": "false",
- "key": "AIzaSyC-P6_qz3FzCoXGLk6tgitZo4jEJ5mLzD8",
- "client_key": "tenor_web",
- "locale": "en",
- "anon_id": "",
- "q": self.search_tags,
- "limit": "50",
- "contentfilter": "low",
- "media_filter": "gif,gif_transparent,mediumgif,tinygif,"
- "tinygif_transparent,webp,webp_transparent,"
- "tinywebp,tinywebp_transparent,tinymp4,mp4,webm,"
- "originalgif,gifpreview",
- "fields": "next,results.id,results.media_formats,results.title,"
- "results.h1_title,results.long_title,results.itemurl,"
- "results.url,results.created,results.user,"
- "results.shares,results.embed,results.hasaudio,"
- "results.policy_status,results.source_id,results.flags,"
- "results.tags,results.content_rating,results.bg_color,"
- "results.legacy_info,results.geographic_restriction,"
- "results.content_description",
- "searchfilter": "none",
- "pos": None,
- "component": "web_desktop",
- }
- headers = {
- "Referer": self.root + "/",
- "Origin" : self.root,
- }
+ return self._search_results(self.search_tags)
- while True:
- data = self.request(url, params=params, headers=headers).json()
- yield from data["results"]
+class TenorUserExtractor(TenorExtractor):
+ subcategory = "user"
+ directory_fmt = ("{category}", "@{user[username]}")
+ pattern = BASE_PATTERN + r"/users/([^/?#]+)"
+ example = "https://tenor.com/users/USER"
- params["pos"] = data.get("next")
- if not params["pos"]:
- return
+ def gifs(self):
+ return self._search_results("@" + self.groups[0])
diff --git a/test/results/tenor.py b/test/results/tenor.py
index fb216822..e5122efd 100644
--- a/test/results/tenor.py
+++ b/test/results/tenor.py
@@ -17,7 +17,7 @@ __tests__ = (
"content_description": "an illustration of a tree with green leaves",
"created" : 1687512768.687436,
"date" : "dt:2023-06-23 09:32:48",
- "embed" : " ",
+ "embed" : r"re:",
},
+{
+ "#url" : "https://tenor.com/users/robloxfan123",
+ "#class" : tenor.TenorUserExtractor,
+ "#urls" : "https://media1.tenor.com/m/1auSjzCikuoAAAAC/2016-roblox.gif",
+
+ "user": {
+ "profile_id": "8180139772821505417",
+ "url" : "https://tenor.com/users/ROBLOXfan123",
+ "userid" : "11206759",
+ "username" : "ROBLOXfan123",
+ "usertype" : "user",
+ },
+},
+
+{
+ "#url" : "https://tenor.com/users/annetv",
+ "#class" : tenor.TenorUserExtractor,
+ "#pattern": r"https://media\d+\.tenor\.com/m/[\w-]+/[\w%-]+\.gif",
+ "#count" : range(70, 100),
+
+ "user": {
+ "profile_id": "14727075564983373376",
+ "url" : "https://tenor.com/users/annetv",
+ "userid" : "8529134",
+ "username" : "annetv",
+ },
+},
+
)