From f7f8ef86848482bafc1f23f9f47e9da12decf0d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20F=C3=A4hrmann?= Date: Tue, 13 Feb 2024 01:17:13 +0100 Subject: [PATCH] [twitter] support communities (#4913) --- docs/supportedsites.md | 2 +- gallery_dl/extractor/twitter.py | 62 ++++++++++++++++++++++++++++++++- scripts/supportedsites.py | 1 + test/results/twitter.py | 16 +++++++++ 4 files changed, 79 insertions(+), 2 deletions(-) diff --git a/docs/supportedsites.md b/docs/supportedsites.md index 6e05898a..c26cfe07 100644 --- a/docs/supportedsites.md +++ b/docs/supportedsites.md @@ -898,7 +898,7 @@ Consider all listed sites to potentially be NSFW. Twitter https://twitter.com/ - Avatars, Backgrounds, Bookmarks, Events, Followed Users, Hashtags, individual Images, Likes, Lists, List Members, Media Timelines, Search Results, Timelines, Tweets, User Profiles + Avatars, Backgrounds, Bookmarks, Communities, Events, Followed Users, Hashtags, individual Images, Likes, Lists, List Members, Media Timelines, Search Results, Timelines, Tweets, User Profiles Supported diff --git a/gallery_dl/extractor/twitter.py b/gallery_dl/extractor/twitter.py index 7cf08170..ad5bfc62 100644 --- a/gallery_dl/extractor/twitter.py +++ b/gallery_dl/extractor/twitter.py @@ -693,6 +693,28 @@ class TwitterHashtagExtractor(TwitterExtractor): yield Message.Queue, url, data +class TwitterCommunityExtractor(TwitterExtractor): + """Extractor for a Twitter community""" + subcategory = "community" + pattern = BASE_PATTERN + r"/i/communities/(\d+)" + example = "https://twitter.com/i/communities/12345" + + def tweets(self): + if self.textonly: + return self.api.community_tweets_timeline(self.user) + return self.api.community_media_timeline(self.user) + + +class TwitterCommunitiesExtractor(TwitterExtractor): + """Extractor for followed Twitter communities""" + subcategory = "communities" + pattern = BASE_PATTERN + r"/([^/?#]+)/communities/?$" + example = "https://twitter.com/i/communities" + + def tweets(self): + return self.api.communities_main_page_timeline(self.user) + + class TwitterEventExtractor(TwitterExtractor): """Extractor for Tweets from a Twitter Event""" subcategory = "event" @@ -1100,6 +1122,43 @@ class TwitterAPI(): endpoint, variables, ("search_by_raw_query", "search_timeline", "timeline")) + def community_tweets_timeline(self, community_id): + endpoint = "/graphql/7B2AdxSuC-Er8qUr3Plm_w/CommunityTweetsTimeline" + variables = { + "communityId": community_id, + "count": 100, + "displayLocation": "Community", + "rankingMode": "Recency", + "withCommunity": True, + } + return self._pagination_tweets( + endpoint, variables, + ("communityResults", "result", "ranked_community_timeline", + "timeline")) + + def community_media_timeline(self, community_id): + endpoint = "/graphql/qAGUldfcIoMv5KyAyVLYog/CommunityMediaTimeline" + variables = { + "communityId": community_id, + "count": 100, + "withCommunity": True, + } + return self._pagination_tweets( + endpoint, variables, + ("communityResults", "result", "community_media_timeline", + "timeline")) + + def communities_main_page_timeline(self, screen_name): + endpoint = ("/graphql/GtOhw2mstITBepTRppL6Uw" + "/CommunitiesMainPageTimeline") + variables = { + "count": 100, + "withCommunity": True, + } + return self._pagination_tweets( + endpoint, variables, + ("viewer", "communities_timeline", "timeline")) + def live_event_timeline(self, event_id): endpoint = "/2/live_event/timeline/{}.json".format(event_id) params = self.params.copy() @@ -1433,7 +1492,8 @@ class TwitterAPI(): if esw("tweet-"): tweets.append(entry) - elif esw("profile-grid-"): + elif esw(("profile-grid-", + "communities-grid-")): if "content" in entry: tweets.extend(entry["content"]["items"]) else: diff --git a/scripts/supportedsites.py b/scripts/supportedsites.py index 98a23234..9001a925 100755 --- a/scripts/supportedsites.py +++ b/scripts/supportedsites.py @@ -284,6 +284,7 @@ SUBCATEGORY_MAP = { "media": "Media Timelines", "tweets": "", "replies": "", + "community": "", "list-members": "List Members", }, "vk": { diff --git a/test/results/twitter.py b/test/results/twitter.py index b7471309..5150a11a 100644 --- a/test/results/twitter.py +++ b/test/results/twitter.py @@ -229,6 +229,22 @@ __tests__ = ( "#count" : ">=1", }, +{ + "#url" : "https://twitter.com/i/communities", + "#category": ("", "twitter", "communities"), + "#class" : twitter.TwitterCommunitiesExtractor, + "#range" : "1-20", + "#count" : 20, +}, + +{ + "#url" : "https://twitter.com/i/communities/1651515740753735697", + "#category": ("", "twitter", "community"), + "#class" : twitter.TwitterCommunityExtractor, + "#range" : "1-20", + "#count" : 20, +}, + { "#url" : "https://twitter.com/supernaturepics/status/604341487988576256", "#comment" : "all Tweets from a 'conversation' (#1319)",