gfycat.py (4218B)
1 # coding: utf-8 2 from __future__ import unicode_literals 3 4 from .common import InfoExtractor 5 from ..utils import ( 6 int_or_none, 7 float_or_none, 8 qualities, 9 ExtractorError, 10 ) 11 12 13 class GfycatIE(InfoExtractor): 14 _VALID_URL = r'https?://(?:(?:www|giant|thumbs)\.)?gfycat\.com/(?:ru/|ifr/|gifs/detail/)?(?P<id>[^-/?#\.]+)' 15 _TESTS = [{ 16 'url': 'http://gfycat.com/DeadlyDecisiveGermanpinscher', 17 'info_dict': { 18 'id': 'DeadlyDecisiveGermanpinscher', 19 'ext': 'mp4', 20 'title': 'Ghost in the Shell', 21 'timestamp': 1410656006, 22 'upload_date': '20140914', 23 'uploader': 'anonymous', 24 'duration': 10.4, 25 'view_count': int, 26 'like_count': int, 27 'dislike_count': int, 28 'categories': list, 29 'age_limit': 0, 30 } 31 }, { 32 'url': 'http://gfycat.com/ifr/JauntyTimelyAmazontreeboa', 33 'info_dict': { 34 'id': 'JauntyTimelyAmazontreeboa', 35 'ext': 'mp4', 36 'title': 'JauntyTimelyAmazontreeboa', 37 'timestamp': 1411720126, 38 'upload_date': '20140926', 39 'uploader': 'anonymous', 40 'duration': 3.52, 41 'view_count': int, 42 'like_count': int, 43 'dislike_count': int, 44 'categories': list, 45 'age_limit': 0, 46 } 47 }, { 48 'url': 'https://gfycat.com/ru/RemarkableDrearyAmurstarfish', 49 'only_matching': True 50 }, { 51 'url': 'https://gfycat.com/gifs/detail/UnconsciousLankyIvorygull', 52 'only_matching': True 53 }, { 54 'url': 'https://gfycat.com/acceptablehappygoluckyharborporpoise-baseball', 55 'only_matching': True 56 }, { 57 'url': 'https://thumbs.gfycat.com/acceptablehappygoluckyharborporpoise-size_restricted.gif', 58 'only_matching': True 59 }, { 60 'url': 'https://giant.gfycat.com/acceptablehappygoluckyharborporpoise.mp4', 61 'only_matching': True 62 }] 63 64 def _real_extract(self, url): 65 video_id = self._match_id(url) 66 67 gfy = self._download_json( 68 'https://api.gfycat.com/v1/gfycats/%s' % video_id, 69 video_id, 'Downloading video info') 70 if 'error' in gfy: 71 raise ExtractorError('Gfycat said: ' + gfy['error'], expected=True) 72 gfy = gfy['gfyItem'] 73 74 title = gfy.get('title') or gfy['gfyName'] 75 description = gfy.get('description') 76 timestamp = int_or_none(gfy.get('createDate')) 77 uploader = gfy.get('userName') 78 view_count = int_or_none(gfy.get('views')) 79 like_count = int_or_none(gfy.get('likes')) 80 dislike_count = int_or_none(gfy.get('dislikes')) 81 age_limit = 18 if gfy.get('nsfw') == '1' else 0 82 83 width = int_or_none(gfy.get('width')) 84 height = int_or_none(gfy.get('height')) 85 fps = int_or_none(gfy.get('frameRate')) 86 num_frames = int_or_none(gfy.get('numFrames')) 87 88 duration = float_or_none(num_frames, fps) if num_frames and fps else None 89 90 categories = gfy.get('tags') or gfy.get('extraLemmas') or [] 91 92 FORMATS = ('gif', 'webm', 'mp4') 93 quality = qualities(FORMATS) 94 95 formats = [] 96 for format_id in FORMATS: 97 video_url = gfy.get('%sUrl' % format_id) 98 if not video_url: 99 continue 100 filesize = int_or_none(gfy.get('%sSize' % format_id)) 101 formats.append({ 102 'url': video_url, 103 'format_id': format_id, 104 'width': width, 105 'height': height, 106 'fps': fps, 107 'filesize': filesize, 108 'quality': quality(format_id), 109 }) 110 self._sort_formats(formats) 111 112 return { 113 'id': video_id, 114 'title': title, 115 'description': description, 116 'timestamp': timestamp, 117 'uploader': uploader, 118 'duration': duration, 119 'view_count': view_count, 120 'like_count': like_count, 121 'dislike_count': dislike_count, 122 'categories': categories, 123 'age_limit': age_limit, 124 'formats': formats, 125 }