# coding: utf-8 from __future__ import unicode_literals from .common import InfoExtractor from ..utils import ( ExtractorError, float_or_none, int_or_none, parse_iso8601, qualities, ) class CoubIE(InfoExtractor): _VALID_URL = r'(?:coub:|https?://(?:coub\.com/(?:view|embed|coubs)/|c-cdn\.coub\.com/fb-player\.swf\?.*\bcoub(?:ID|id)=))(?P[\da-z]+)' _TESTS = [{ 'url': 'http://coub.com/view/5u5n1', 'info_dict': { 'id': '5u5n1', 'ext': 'mp4', 'title': 'The Matrix Moonwalk', 'thumbnail': r're:^https?://.*\.jpg$', 'duration': 4.6, 'timestamp': 1428527772, 'upload_date': '20150408', 'uploader': 'Artyom Loskutnikov', 'uploader_id': 'artyom.loskutnikov', 'view_count': int, 'like_count': int, 'repost_count': int, 'age_limit': 0, }, }, { 'url': 'http://c-cdn.coub.com/fb-player.swf?bot_type=vk&coubID=7w5a4', 'only_matching': True, }, { 'url': 'coub:5u5n1', 'only_matching': True, }, { # longer video id 'url': 'http://coub.com/view/237d5l5h', 'only_matching': True, }] def _real_extract(self, url): video_id = self._match_id(url) coub = self._download_json( 'http://coub.com/api/v2/coubs/%s.json' % video_id, video_id) if coub.get('error'): raise ExtractorError( '%s said: %s' % (self.IE_NAME, coub['error']), expected=True) title = coub['title'] file_versions = coub['file_versions'] QUALITIES = ('low', 'med', 'high') MOBILE = 'mobile' IPHONE = 'iphone' HTML5 = 'html5' SOURCE_PREFERENCE = (MOBILE, IPHONE, HTML5) quality_key = qualities(QUALITIES) preference_key = qualities(SOURCE_PREFERENCE) formats = [] for kind, items in file_versions.get(HTML5, {}).items(): if kind not in ('video', 'audio'): continue if not isinstance(items, dict): continue for quality, item in items.items(): if not isinstance(item, dict): continue item_url = item.get('url') if not item_url: continue formats.append({ 'url': item_url, 'format_id': '%s-%s-%s' % (HTML5, kind, quality), 'filesize': int_or_none(item.get('size')), 'vcodec': 'none' if kind == 'audio' else None, 'quality': quality_key(quality), 'preference': preference_key(HTML5), }) iphone_url = file_versions.get(IPHONE, {}).get('url') if iphone_url: formats.append({ 'url': iphone_url, 'format_id': IPHONE, 'preference': preference_key(IPHONE), }) mobile_url = file_versions.get(MOBILE, {}).get('audio_url') if mobile_url: formats.append({ 'url': mobile_url, 'format_id': '%s-audio' % MOBILE, 'preference': preference_key(MOBILE), }) self._sort_formats(formats) thumbnail = coub.get('picture') duration = float_or_none(coub.get('duration')) timestamp = parse_iso8601(coub.get('published_at') or coub.get('created_at')) uploader = coub.get('channel', {}).get('title') uploader_id = coub.get('channel', {}).get('permalink') view_count = int_or_none(coub.get('views_count') or coub.get('views_increase_count')) like_count = int_or_none(coub.get('likes_count')) repost_count = int_or_none(coub.get('recoubs_count')) age_restricted = coub.get('age_restricted', coub.get('age_restricted_by_admin')) if age_restricted is not None: age_limit = 18 if age_restricted is True else 0 else: age_limit = None return { 'id': video_id, 'title': title, 'thumbnail': thumbnail, 'duration': duration, 'timestamp': timestamp, 'uploader': uploader, 'uploader_id': uploader_id, 'view_count': view_count, 'like_count': like_count, 'repost_count': repost_count, 'age_limit': age_limit, 'formats': formats, }