3 from .common
import InfoExtractor
4 from ..compat
import compat_parse_qs
14 class RedGifsBaseInfoExtractor(InfoExtractor
):
21 def _parse_gif_data(self
, gif_data
):
22 video_id
= gif_data
.get('id')
23 quality
= qualities(tuple(self
._FORMATS
.keys()))
25 orig_height
= int_or_none(gif_data
.get('height'))
26 aspect_ratio
= try_get(gif_data
, lambda x
: orig_height
/ x
['width'])
29 for format_id
, height
in self
._FORMATS
.items():
30 video_url
= gif_data
['urls'].get(format_id
)
33 height
= min(orig_height
, height
or orig_height
)
36 'format_id': format_id
,
37 'width': height
* aspect_ratio
if aspect_ratio
else None,
39 'quality': quality(format_id
),
41 self
._sort
_formats
(formats
)
45 'webpage_url': f
'https://redgifs.com/watch/{video_id}',
46 'ie_key': RedGifsIE
.ie_key(),
47 'extractor': 'RedGifs',
48 'title': ' '.join(gif_data
.get('tags') or []) or 'RedGifs',
49 'timestamp': int_or_none(gif_data
.get('createDate')),
50 'uploader': gif_data
.get('userName'),
51 'duration': int_or_none(gif_data
.get('duration')),
52 'view_count': int_or_none(gif_data
.get('views')),
53 'like_count': int_or_none(gif_data
.get('likes')),
54 'categories': gif_data
.get('tags') or [],
55 'tags': gif_data
.get('tags'),
60 def _call_api(self
, ep
, video_id
, *args
, **kwargs
):
61 data
= self
._download
_json
(
62 f
'https://api.redgifs.com/v2/{ep}', video_id
, *args
, **kwargs
)
64 raise ExtractorError(f
'RedGifs said: {data["error"]}', expected
=True, video_id
=video_id
)
67 def _fetch_page(self
, ep
, video_id
, query
, page
):
68 query
['page'] = page
+ 1
69 data
= self
._call
_api
(
70 ep
, video_id
, query
=query
, note
=f
'Downloading JSON metadata page {page + 1}')
72 for entry
in data
['gifs']:
73 yield self
._parse
_gif
_data
(entry
)
75 def _prepare_api_query(self
, query
, fields
):
77 (field_name
, query
.get(field_name
, (default
,))[0])
78 for field_name
, default
in fields
.items()]
80 return {key: val for key, val in api_query if val is not None}
82 def _paged_entries(self
, ep
, item_id
, query
, fields
):
83 page
= int_or_none(query
.get('page', (None,))[0])
84 page_fetcher
= functools
.partial(
85 self
._fetch
_page
, ep
, item_id
, self
._prepare
_api
_query
(query
, fields
))
86 return page_fetcher(page
) if page
else OnDemandPagedList(page_fetcher
, self
._PAGE
_SIZE
)
89 class RedGifsIE(RedGifsBaseInfoExtractor
):
90 _VALID_URL
= r
'https?://(?:(?:www\.)?redgifs\.com/watch/|thumbs2\.redgifs\.com/)(?P<id>[^-/?#\.]+)'
92 'url': 'https://www.redgifs.com/watch/squeakyhelplesswisent',
94 'id': 'squeakyhelplesswisent',
96 'title': 'Hotwife Legs Thick',
97 'timestamp': 1636287915,
98 'upload_date': '20211107',
99 'uploader': 'ignored52',
107 'url': 'https://thumbs2.redgifs.com/SqueakyHelplessWisent-mobile.mp4#t=0',
109 'id': 'squeakyhelplesswisent',
111 'title': 'Hotwife Legs Thick',
112 'timestamp': 1636287915,
113 'upload_date': '20211107',
114 'uploader': 'ignored52',
123 def _real_extract(self
, url
):
124 video_id
= self
._match
_id
(url
).lower()
125 video_info
= self
._call
_api
(
126 f
'gifs/{video_id}', video_id
, note
='Downloading video info')
127 return self
._parse
_gif
_data
(video_info
['gif'])
130 class RedGifsSearchIE(RedGifsBaseInfoExtractor
):
131 IE_DESC
= 'Redgifs search'
132 _VALID_URL
= r
'https?://(?:www\.)?redgifs\.com/browse\?(?P<query>[^#]+)'
136 'url': 'https://www.redgifs.com/browse?tags=Lesbian',
138 'id': 'tags=Lesbian',
140 'description': 'RedGifs search for Lesbian, ordered by trending'
142 'playlist_mincount': 100,
145 'url': 'https://www.redgifs.com/browse?type=g&order=latest&tags=Lesbian',
147 'id': 'type=g&order=latest&tags=Lesbian',
149 'description': 'RedGifs search for Lesbian, ordered by latest'
151 'playlist_mincount': 100,
154 'url': 'https://www.redgifs.com/browse?type=g&order=latest&tags=Lesbian&page=2',
156 'id': 'type=g&order=latest&tags=Lesbian&page=2',
158 'description': 'RedGifs search for Lesbian, ordered by latest'
160 'playlist_count': 80,
164 def _real_extract(self
, url
):
165 query_str
= self
._match
_valid
_url
(url
).group('query')
166 query
= compat_parse_qs(query_str
)
167 if not query
.get('tags'):
168 raise ExtractorError('Invalid query tags', expected
=True)
170 tags
= query
.get('tags')[0]
171 order
= query
.get('order', ('trending',))[0]
173 query
['search_text'] = [tags
]
174 entries
= self
._paged
_entries
('gifs/search', query_str
, query
, {
180 return self
.playlist_result(
181 entries
, query_str
, tags
, f
'RedGifs search for {tags}, ordered by {order}')
184 class RedGifsUserIE(RedGifsBaseInfoExtractor
):
185 IE_DESC
= 'Redgifs user'
186 _VALID_URL
= r
'https?://(?:www\.)?redgifs\.com/users/(?P<username>[^/?#]+)(?:\?(?P<query>[^#]+))?'
190 'url': 'https://www.redgifs.com/users/lamsinka89',
193 'title': 'lamsinka89',
194 'description': 'RedGifs user lamsinka89, ordered by recent'
196 'playlist_mincount': 100,
199 'url': 'https://www.redgifs.com/users/lamsinka89?page=3',
201 'id': 'lamsinka89?page=3',
202 'title': 'lamsinka89',
203 'description': 'RedGifs user lamsinka89, ordered by recent'
205 'playlist_count': 30,
208 'url': 'https://www.redgifs.com/users/lamsinka89?order=best&type=g',
210 'id': 'lamsinka89?order=best&type=g',
211 'title': 'lamsinka89',
212 'description': 'RedGifs user lamsinka89, ordered by best'
214 'playlist_mincount': 100,
218 def _real_extract(self
, url
):
219 username
, query_str
= self
._match
_valid
_url
(url
).group('username', 'query')
220 playlist_id
= f
'{username}?{query_str}' if query_str
else username
222 query
= compat_parse_qs(query_str
)
223 order
= query
.get('order', ('recent',))[0]
225 entries
= self
._paged
_entries
(f
'users/{username}/search', playlist_id
, query
, {
230 return self
.playlist_result(
231 entries
, playlist_id
, username
, f
'RedGifs user {username}, ordered by {order}')