+
+
+class TwitchClipsIE(InfoExtractor):
+ IE_NAME = 'twitch:clips'
+ _VALID_URL = r'https?://clips\.twitch\.tv/(?:[^/]+/)*(?P<id>[^/?#&]+)'
+
+ _TESTS = [{
+ 'url': 'https://clips.twitch.tv/ea/AggressiveCobraPoooound',
+ 'md5': '761769e1eafce0ffebfb4089cb3847cd',
+ 'info_dict': {
+ 'id': 'AggressiveCobraPoooound',
+ 'ext': 'mp4',
+ 'title': 'EA Play 2016 Live from the Novo Theatre',
+ 'thumbnail': 're:^https?://.*\.jpg',
+ 'creator': 'EA',
+ 'uploader': 'stereotype_',
+ 'uploader_id': 'stereotype_',
+ },
+ }, {
+ # multiple formats
+ 'url': 'https://clips.twitch.tv/rflegendary/UninterestedBeeDAESuppy',
+ 'only_matching': True,
+ }]
+
+ def _real_extract(self, url):
+ video_id = self._match_id(url)
+
+ webpage = self._download_webpage(url, video_id)
+
+ clip = self._parse_json(
+ self._search_regex(
+ r'(?s)clipInfo\s*=\s*({.+?});', webpage, 'clip info'),
+ video_id, transform_source=js_to_json)
+
+ title = clip.get('channel_title') or self._og_search_title(webpage)
+
+ formats = [{
+ 'url': option['source'],
+ 'format_id': option.get('quality'),
+ 'height': int_or_none(option.get('quality')),
+ } for option in clip.get('quality_options', []) if option.get('source')]
+
+ if not formats:
+ formats = [{
+ 'url': clip['clip_video_url'],
+ }]
+
+ self._sort_formats(formats)
+
+ return {
+ 'id': video_id,
+ 'title': title,
+ 'thumbnail': self._og_search_thumbnail(webpage),
+ 'creator': clip.get('broadcaster_display_name') or clip.get('broadcaster_login'),
+ 'uploader': clip.get('curator_login'),
+ 'uploader_id': clip.get('curator_display_name'),
+ 'formats': formats,
+ }