4 from .common
import InfoExtractor
5 from ..compat
import compat_parse_qs
15 class RedGifsBaseInfoExtractor(InfoExtractor
):
22 def _parse_gif_data(self
, gif_data
):
23 video_id
= gif_data
.get('id')
24 quality
= qualities(tuple(self
._FORMATS
.keys()))
26 orig_height
= int_or_none(gif_data
.get('height'))
27 aspect_ratio
= try_get(gif_data
, lambda x
: orig_height
/ x
['width'])
30 for format_id
, height
in self
._FORMATS
.items():
31 video_url
= gif_data
['urls'].get(format_id
)
34 height
= min(orig_height
, height
or orig_height
)
37 'format_id': format_id
,
38 'width': height
* aspect_ratio
if aspect_ratio
else None,
40 'quality': quality(format_id
),
42 self
._sort
_formats
(formats
)
46 'webpage_url': f
'https://redgifs.com/watch/{video_id}',
47 'ie_key': RedGifsIE
.ie_key(),
48 'extractor': 'RedGifs',
49 'title': ' '.join(gif_data
.get('tags') or []) or 'RedGifs',
50 'timestamp': int_or_none(gif_data
.get('createDate')),
51 'uploader': gif_data
.get('userName'),
52 'duration': int_or_none(gif_data
.get('duration')),
53 'view_count': int_or_none(gif_data
.get('views')),
54 'like_count': int_or_none(gif_data
.get('likes')),
55 'categories': gif_data
.get('tags') or [],
56 'tags': gif_data
.get('tags'),
61 def _call_api(self
, ep
, video_id
, *args
, **kwargs
):
62 data
= self
._download
_json
(
63 f
'https://api.redgifs.com/v2/{ep}', video_id
, *args
, **kwargs
)
65 raise ExtractorError(f
'RedGifs said: {data["error"]}', expected
=True, video_id
=video_id
)
68 def _fetch_page(self
, ep
, video_id
, query
, page
):
69 query
['page'] = page
+ 1
70 data
= self
._call
_api
(
71 ep
, video_id
, query
=query
, note
=f
'Downloading JSON metadata page {page + 1}')
73 for entry
in data
['gifs']:
74 yield self
._parse
_gif
_data
(entry
)
76 def _prepare_api_query(self
, query
, fields
):
78 (field_name
, query
.get(field_name
, (default
,))[0])
79 for field_name
, default
in fields
.items()]
81 return {key: val for key, val in api_query if val is not None}
83 def _paged_entries(self
, ep
, item_id
, query
, fields
):
84 page
= int_or_none(query
.get('page', (None,))[0])
85 page_fetcher
= functools
.partial(
86 self
._fetch
_page
, ep
, item_id
, self
._prepare
_api
_query
(query
, fields
))
87 return page_fetcher(page
) if page
else OnDemandPagedList(page_fetcher
, self
._PAGE
_SIZE
)
90 class RedGifsIE(RedGifsBaseInfoExtractor
):
91 _VALID_URL
= r
'https?://(?:(?:www\.)?redgifs\.com/watch/|thumbs2\.redgifs\.com/)(?P<id>[^-/?#\.]+)'
93 'url': 'https://www.redgifs.com/watch/squeakyhelplesswisent',
95 'id': 'squeakyhelplesswisent',
97 'title': 'Hotwife Legs Thick',
98 'timestamp': 1636287915,
99 'upload_date': '20211107',
100 'uploader': 'ignored52',
108 'url': 'https://thumbs2.redgifs.com/SqueakyHelplessWisent-mobile.mp4#t=0',
110 'id': 'squeakyhelplesswisent',
112 'title': 'Hotwife Legs Thick',
113 'timestamp': 1636287915,
114 'upload_date': '20211107',
115 'uploader': 'ignored52',
124 def _real_extract(self
, url
):
125 video_id
= self
._match
_id
(url
).lower()
126 video_info
= self
._call
_api
(
127 f
'gifs/{video_id}', video_id
, note
='Downloading video info')
128 return self
._parse
_gif
_data
(video_info
['gif'])
131 class RedGifsSearchIE(RedGifsBaseInfoExtractor
):
132 IE_DESC
= 'Redgifs search'
133 _VALID_URL
= r
'https?://(?:www\.)?redgifs\.com/browse\?(?P<query>[^#]+)'
137 'url': 'https://www.redgifs.com/browse?tags=Lesbian',
139 'id': 'tags=Lesbian',
141 'description': 'RedGifs search for Lesbian, ordered by trending'
143 'playlist_mincount': 100,
146 'url': 'https://www.redgifs.com/browse?type=g&order=latest&tags=Lesbian',
148 'id': 'type=g&order=latest&tags=Lesbian',
150 'description': 'RedGifs search for Lesbian, ordered by latest'
152 'playlist_mincount': 100,
155 'url': 'https://www.redgifs.com/browse?type=g&order=latest&tags=Lesbian&page=2',
157 'id': 'type=g&order=latest&tags=Lesbian&page=2',
159 'description': 'RedGifs search for Lesbian, ordered by latest'
161 'playlist_count': 80,
165 def _real_extract(self
, url
):
166 query_str
= self
._match
_valid
_url
(url
).group('query')
167 query
= compat_parse_qs(query_str
)
168 if not query
.get('tags'):
169 raise ExtractorError('Invalid query tags', expected
=True)
171 tags
= query
.get('tags')[0]
172 order
= query
.get('order', ('trending',))[0]
174 query
['search_text'] = [tags
]
175 entries
= self
._paged
_entries
('gifs/search', query_str
, query
, {
181 return self
.playlist_result(
182 entries
, query_str
, tags
, f
'RedGifs search for {tags}, ordered by {order}')
185 class RedGifsUserIE(RedGifsBaseInfoExtractor
):
186 IE_DESC
= 'Redgifs user'
187 _VALID_URL
= r
'https?://(?:www\.)?redgifs\.com/users/(?P<username>[^/?#]+)(?:\?(?P<query>[^#]+))?'
191 'url': 'https://www.redgifs.com/users/lamsinka89',
194 'title': 'lamsinka89',
195 'description': 'RedGifs user lamsinka89, ordered by recent'
197 'playlist_mincount': 100,
200 'url': 'https://www.redgifs.com/users/lamsinka89?page=3',
202 'id': 'lamsinka89?page=3',
203 'title': 'lamsinka89',
204 'description': 'RedGifs user lamsinka89, ordered by recent'
206 'playlist_count': 30,
209 'url': 'https://www.redgifs.com/users/lamsinka89?order=best&type=g',
211 'id': 'lamsinka89?order=best&type=g',
212 'title': 'lamsinka89',
213 'description': 'RedGifs user lamsinka89, ordered by best'
215 'playlist_mincount': 100,
219 def _real_extract(self
, url
):
220 username
, query_str
= self
._match
_valid
_url
(url
).group('username', 'query')
221 playlist_id
= f
'{username}?{query_str}' if query_str
else username
223 query
= compat_parse_qs(query_str
)
224 order
= query
.get('order', ('recent',))[0]
226 entries
= self
._paged
_entries
(f
'users/{username}/search', playlist_id
, query
, {
231 return self
.playlist_result(
232 entries
, playlist_id
, username
, f
'RedGifs user {username}, ordered by {order}')