arkena.py (7525B)
1 # coding: utf-8 2 from __future__ import unicode_literals 3 4 import re 5 6 from .common import InfoExtractor 7 from ..compat import compat_urlparse 8 from ..utils import ( 9 ExtractorError, 10 float_or_none, 11 int_or_none, 12 parse_iso8601, 13 try_get, 14 ) 15 16 17 class ArkenaIE(InfoExtractor): 18 _VALID_URL = r'''(?x) 19 https?:// 20 (?: 21 video\.(?:arkena|qbrick)\.com/play2/embed/player\?| 22 play\.arkena\.com/(?:config|embed)/avp/v\d/player/media/(?P<id>[^/]+)/[^/]+/(?P<account_id>\d+) 23 ) 24 ''' 25 _TESTS = [{ 26 'url': 'https://video.qbrick.com/play2/embed/player?accountId=1034090&mediaId=d8ab4607-00090107-aab86310', 27 'md5': '97f117754e5f3c020f5f26da4a44ebaf', 28 'info_dict': { 29 'id': 'd8ab4607-00090107-aab86310', 30 'ext': 'mp4', 31 'title': 'EM_HT20_117_roslund_v2.mp4', 32 'timestamp': 1608285912, 33 'upload_date': '20201218', 34 'duration': 1429.162667, 35 'subtitles': { 36 'sv': 'count:3', 37 }, 38 }, 39 }, { 40 'url': 'https://play.arkena.com/embed/avp/v2/player/media/b41dda37-d8e7-4d3f-b1b5-9a9db578bdfe/1/129411', 41 'only_matching': True, 42 }, { 43 'url': 'https://play.arkena.com/config/avp/v2/player/media/b41dda37-d8e7-4d3f-b1b5-9a9db578bdfe/1/129411/?callbackMethod=jQuery1111023664739129262213_1469227693893', 44 'only_matching': True, 45 }, { 46 'url': 'http://play.arkena.com/config/avp/v1/player/media/327336/darkmatter/131064/?callbackMethod=jQuery1111002221189684892677_1469227595972', 47 'only_matching': True, 48 }, { 49 'url': 'http://play.arkena.com/embed/avp/v1/player/media/327336/darkmatter/131064/', 50 'only_matching': True, 51 }, { 52 'url': 'http://video.arkena.com/play2/embed/player?accountId=472718&mediaId=35763b3b-00090078-bf604299&pageStyling=styled', 53 'only_matching': True, 54 }] 55 56 @staticmethod 57 def _extract_url(webpage): 58 # See https://support.arkena.com/display/PLAY/Ways+to+embed+your+video 59 mobj = re.search( 60 r'<iframe[^>]+src=(["\'])(?P<url>(?:https?:)?//play\.arkena\.com/embed/avp/.+?)\1', 61 webpage) 62 if mobj: 63 return mobj.group('url') 64 65 def _real_extract(self, url): 66 mobj = re.match(self._VALID_URL, url) 67 video_id = mobj.group('id') 68 account_id = mobj.group('account_id') 69 70 # Handle http://video.arkena.com/play2/embed/player URL 71 if not video_id: 72 qs = compat_urlparse.parse_qs(compat_urlparse.urlparse(url).query) 73 video_id = qs.get('mediaId', [None])[0] 74 account_id = qs.get('accountId', [None])[0] 75 if not video_id or not account_id: 76 raise ExtractorError('Invalid URL', expected=True) 77 78 media = self._download_json( 79 'https://video.qbrick.com/api/v1/public/accounts/%s/medias/%s' % (account_id, video_id), 80 video_id, query={ 81 # https://video.qbrick.com/docs/api/examples/library-api.html 82 'fields': 'asset/resources/*/renditions/*(height,id,language,links/*(href,mimeType),type,size,videos/*(audios/*(codec,sampleRate),bitrate,codec,duration,height,width),width),created,metadata/*(title,description),tags', 83 }) 84 metadata = media.get('metadata') or {} 85 title = metadata['title'] 86 87 duration = None 88 formats = [] 89 thumbnails = [] 90 subtitles = {} 91 for resource in media['asset']['resources']: 92 for rendition in (resource.get('renditions') or []): 93 rendition_type = rendition.get('type') 94 for i, link in enumerate(rendition.get('links') or []): 95 href = link.get('href') 96 if not href: 97 continue 98 if rendition_type == 'image': 99 thumbnails.append({ 100 'filesize': int_or_none(rendition.get('size')), 101 'height': int_or_none(rendition.get('height')), 102 'id': rendition.get('id'), 103 'url': href, 104 'width': int_or_none(rendition.get('width')), 105 }) 106 elif rendition_type == 'subtitle': 107 subtitles.setdefault(rendition.get('language') or 'en', []).append({ 108 'url': href, 109 }) 110 elif rendition_type == 'video': 111 f = { 112 'filesize': int_or_none(rendition.get('size')), 113 'format_id': rendition.get('id'), 114 'url': href, 115 } 116 video = try_get(rendition, lambda x: x['videos'][i], dict) 117 if video: 118 if not duration: 119 duration = float_or_none(video.get('duration')) 120 f.update({ 121 'height': int_or_none(video.get('height')), 122 'tbr': int_or_none(video.get('bitrate'), 1000), 123 'vcodec': video.get('codec'), 124 'width': int_or_none(video.get('width')), 125 }) 126 audio = try_get(video, lambda x: x['audios'][0], dict) 127 if audio: 128 f.update({ 129 'acodec': audio.get('codec'), 130 'asr': int_or_none(audio.get('sampleRate')), 131 }) 132 formats.append(f) 133 elif rendition_type == 'index': 134 mime_type = link.get('mimeType') 135 if mime_type == 'application/smil+xml': 136 formats.extend(self._extract_smil_formats( 137 href, video_id, fatal=False)) 138 elif mime_type == 'application/x-mpegURL': 139 formats.extend(self._extract_m3u8_formats( 140 href, video_id, 'mp4', 'm3u8_native', 141 m3u8_id='hls', fatal=False)) 142 elif mime_type == 'application/hds+xml': 143 formats.extend(self._extract_f4m_formats( 144 href, video_id, f4m_id='hds', fatal=False)) 145 elif mime_type == 'application/dash+xml': 146 formats.extend(self._extract_f4m_formats( 147 href, video_id, f4m_id='hds', fatal=False)) 148 elif mime_type == 'application/vnd.ms-sstr+xml': 149 formats.extend(self._extract_ism_formats( 150 href, video_id, ism_id='mss', fatal=False)) 151 self._sort_formats(formats) 152 153 return { 154 'id': video_id, 155 'title': title, 156 'description': metadata.get('description'), 157 'timestamp': parse_iso8601(media.get('created')), 158 'thumbnails': thumbnails, 159 'subtitles': subtitles, 160 'duration': duration, 161 'tags': media.get('tags'), 162 'formats': formats, 163 }