coub.py (4555B)
1 # coding: utf-8 2 from __future__ import unicode_literals 3 4 from .common import InfoExtractor 5 from ..utils import ( 6 ExtractorError, 7 float_or_none, 8 int_or_none, 9 parse_iso8601, 10 qualities, 11 ) 12 13 14 class CoubIE(InfoExtractor): 15 _VALID_URL = r'(?:coub:|https?://(?:coub\.com/(?:view|embed|coubs)/|c-cdn\.coub\.com/fb-player\.swf\?.*\bcoub(?:ID|id)=))(?P<id>[\da-z]+)' 16 17 _TESTS = [{ 18 'url': 'http://coub.com/view/5u5n1', 19 'info_dict': { 20 'id': '5u5n1', 21 'ext': 'mp4', 22 'title': 'The Matrix Moonwalk', 23 'thumbnail': r're:^https?://.*\.jpg$', 24 'duration': 4.6, 25 'timestamp': 1428527772, 26 'upload_date': '20150408', 27 'uploader': 'Artyom Loskutnikov', 28 'uploader_id': 'artyom.loskutnikov', 29 'view_count': int, 30 'like_count': int, 31 'repost_count': int, 32 'age_limit': 0, 33 }, 34 }, { 35 'url': 'http://c-cdn.coub.com/fb-player.swf?bot_type=vk&coubID=7w5a4', 36 'only_matching': True, 37 }, { 38 'url': 'coub:5u5n1', 39 'only_matching': True, 40 }, { 41 # longer video id 42 'url': 'http://coub.com/view/237d5l5h', 43 'only_matching': True, 44 }] 45 46 def _real_extract(self, url): 47 video_id = self._match_id(url) 48 49 coub = self._download_json( 50 'http://coub.com/api/v2/coubs/%s.json' % video_id, video_id) 51 52 if coub.get('error'): 53 raise ExtractorError( 54 '%s said: %s' % (self.IE_NAME, coub['error']), expected=True) 55 56 title = coub['title'] 57 58 file_versions = coub['file_versions'] 59 60 QUALITIES = ('low', 'med', 'high') 61 62 MOBILE = 'mobile' 63 IPHONE = 'iphone' 64 HTML5 = 'html5' 65 66 SOURCE_PREFERENCE = (MOBILE, IPHONE, HTML5) 67 68 quality_key = qualities(QUALITIES) 69 preference_key = qualities(SOURCE_PREFERENCE) 70 71 formats = [] 72 73 for kind, items in file_versions.get(HTML5, {}).items(): 74 if kind not in ('video', 'audio'): 75 continue 76 if not isinstance(items, dict): 77 continue 78 for quality, item in items.items(): 79 if not isinstance(item, dict): 80 continue 81 item_url = item.get('url') 82 if not item_url: 83 continue 84 formats.append({ 85 'url': item_url, 86 'format_id': '%s-%s-%s' % (HTML5, kind, quality), 87 'filesize': int_or_none(item.get('size')), 88 'vcodec': 'none' if kind == 'audio' else None, 89 'quality': quality_key(quality), 90 'preference': preference_key(HTML5), 91 }) 92 93 iphone_url = file_versions.get(IPHONE, {}).get('url') 94 if iphone_url: 95 formats.append({ 96 'url': iphone_url, 97 'format_id': IPHONE, 98 'preference': preference_key(IPHONE), 99 }) 100 101 mobile_url = file_versions.get(MOBILE, {}).get('audio_url') 102 if mobile_url: 103 formats.append({ 104 'url': mobile_url, 105 'format_id': '%s-audio' % MOBILE, 106 'preference': preference_key(MOBILE), 107 }) 108 109 self._sort_formats(formats) 110 111 thumbnail = coub.get('picture') 112 duration = float_or_none(coub.get('duration')) 113 timestamp = parse_iso8601(coub.get('published_at') or coub.get('created_at')) 114 uploader = coub.get('channel', {}).get('title') 115 uploader_id = coub.get('channel', {}).get('permalink') 116 117 view_count = int_or_none(coub.get('views_count') or coub.get('views_increase_count')) 118 like_count = int_or_none(coub.get('likes_count')) 119 repost_count = int_or_none(coub.get('recoubs_count')) 120 121 age_restricted = coub.get('age_restricted', coub.get('age_restricted_by_admin')) 122 if age_restricted is not None: 123 age_limit = 18 if age_restricted is True else 0 124 else: 125 age_limit = None 126 127 return { 128 'id': video_id, 129 'title': title, 130 'thumbnail': thumbnail, 131 'duration': duration, 132 'timestamp': timestamp, 133 'uploader': uploader, 134 'uploader_id': uploader_id, 135 'view_count': view_count, 136 'like_count': like_count, 137 'repost_count': repost_count, 138 'age_limit': age_limit, 139 'formats': formats, 140 }