2 from __future__ 
import unicode_literals
 
   6 from .common 
import InfoExtractor
 
   8     compat_urllib_parse_urlparse
, 
  20 class AfreecaTVIE(InfoExtractor
): 
  22     IE_DESC 
= 'afreecatv.com' 
  26                             (?:(?:live|afbbs|www)\.)?afreeca(?:tv)?\.com(?::\d+)? 
  28                                 /app/(?:index|read_ucc_bbs)\.cgi| 
  29                                 /player/[Pp]layer\.(?:swf|html) 
  31                             vod\.afreecatv\.com/PLAYER/STATION/ 
  36         'url': 'http://live.afreecatv.com:8079/app/index.cgi?szType=read_ucc_bbs&szBjId=dailyapril&nStationNo=16711924&nBbsNo=18605867&nTitleNo=36164052&szSkin=', 
  37         'md5': 'f72c89fe7ecc14c1b5ce506c4996046e', 
  41             'title': '데일리 에이프릴 요정들의 시상식!', 
  42             'thumbnail': 're:^https?://(?:video|st)img.afreecatv.com/.*$', 
  43             'uploader': 'dailyapril', 
  44             'uploader_id': 'dailyapril', 
  45             'upload_date': '20160503', 
  48         'url': 'http://afbbs.afreecatv.com:8080/app/read_ucc_bbs.cgi?nStationNo=16711924&nTitleNo=36153164&szBjId=dailyapril&nBbsNo=18605867', 
  51             'title': "BJ유트루와 함께하는 '팅커벨 메이크업!'", 
  52             'thumbnail': 're:^https?://(?:video|st)img.afreecatv.com/.*$', 
  53             'uploader': 'dailyapril', 
  54             'uploader_id': 'dailyapril', 
  58             'md5': 'd8b7c174568da61d774ef0203159bf97', 
  62                 'title': "BJ유트루와 함께하는 '팅커벨 메이크업!'", 
  63                 'upload_date': '20160502', 
  66             'md5': '58f2ce7f6044e34439ab2d50612ab02b', 
  70                 'title': "BJ유트루와 함께하는 '팅커벨 메이크업!'", 
  71                 'upload_date': '20160502', 
  75         'url': 'http://www.afreecatv.com/player/Player.swf?szType=szBjId=djleegoon&nStationNo=11273158&nBbsNo=13161095&nTitleNo=36327652', 
  76         'only_matching': True, 
  78         'url': 'http://vod.afreecatv.com/PLAYER/STATION/15055030', 
  79         'only_matching': True, 
  83     def parse_video_key(key
): 
  85         m 
= re
.match(r
'^(?P<upload_date>\d{8})_\w+_(?P<part>\d+)$', key
) 
  87             video_key
['upload_date'] = m
.group('upload_date') 
  88             video_key
['part'] = m
.group('part') 
  91     def _real_extract(self
, url
): 
  92         video_id 
= self
._match
_id
(url
) 
  93         parsed_url 
= compat_urllib_parse_urlparse(url
) 
  94         info_url 
= compat_urlparse
.urlunparse(parsed_url
._replace
( 
  95             netloc
='afbbs.afreecatv.com:8080', 
  96             path
='/api/video/get_video_info.php')) 
  98         video_xml 
= self
._download
_xml
( 
  99             update_url_query(info_url
, {'nTitleNo': video_id
}), video_id
) 
 101         if xpath_element(video_xml
, './track/video/file') is None: 
 102             raise ExtractorError('Specified AfreecaTV video does not exist', 
 105         title 
= xpath_text(video_xml
, './track/title', 'title') 
 106         uploader 
= xpath_text(video_xml
, './track/nickname', 'uploader') 
 107         uploader_id 
= xpath_text(video_xml
, './track/bj_id', 'uploader id') 
 108         duration 
= int_or_none(xpath_text(video_xml
, './track/duration', 
 110         thumbnail 
= xpath_text(video_xml
, './track/titleImage', 'thumbnail') 
 113         for i
, video_file 
in enumerate(video_xml
.findall('./track/video/file')): 
 114             video_key 
= self
.parse_video_key(video_file
.get('key', '')) 
 118                 'id': '%s_%s' % (video_id
, video_key
.get('part', i 
+ 1)), 
 120                 'upload_date': video_key
.get('upload_date'), 
 121                 'duration': int_or_none(video_file
.get('duration')), 
 122                 'url': video_file
.text
, 
 128             'uploader': uploader
, 
 129             'uploader_id': uploader_id
, 
 130             'duration': duration
, 
 131             'thumbnail': thumbnail
, 
 135             info
['_type'] = 'multi_video' 
 136             info
['entries'] = entries
 
 137         elif len(entries
) == 1: 
 138             info
['url'] = entries
[0]['url'] 
 139             info
['upload_date'] = entries
[0].get('upload_date') 
 141             raise ExtractorError( 
 142                 'No files found for the specified AfreecaTV video, either' 
 143                 ' the URL is incorrect or the video has been made private.', 
 149 class AfreecaTVGlobalIE(AfreecaTVIE
): 
 150     IE_NAME 
= 'afreecatv:global' 
 151     _VALID_URL 
= r
'https?://(?:www\.)?afreeca\.tv/(?P<channel_id>\d+)(?:/v/(?P<video_id>\d+))?' 
 153         'url': 'http://afreeca.tv/36853014/v/58301', 
 156             'title': 'tryhard top100', 
 157             'uploader_id': '36853014', 
 158             'uploader': 'makgi Hearthstone Live!', 
 163     def _real_extract(self
, url
): 
 164         channel_id
, video_id 
= re
.match(self
._VALID
_URL
, url
).groups() 
 165         video_type 
= 'video' if video_id 
else 'live' 
 171             query
['vno'] = video_id
 
 172         video_data 
= self
._download
_json
( 
 173             'http://api.afreeca.tv/%s/view_%s.php' % (video_type
, video_type
), 
 174             video_id 
or channel_id
, query
=query
)['channel'] 
 176         if video_data
.get('result') != 1: 
 177             raise ExtractorError('%s said: %s' % (self
.IE_NAME
, video_data
['remsg'])) 
 179         title 
= video_data
['title'] 
 182             'thumbnail': video_data
.get('thumb'), 
 183             'view_count': int_or_none(video_data
.get('vcnt')), 
 184             'age_limit': int_or_none(video_data
.get('grade')), 
 185             'uploader_id': channel_id
, 
 186             'uploader': video_data
.get('cname'), 
 191             for i
, f 
in enumerate(video_data
.get('flist', [])): 
 192                 video_key 
= self
.parse_video_key(f
.get('key', '')) 
 193                 f_url 
= f
.get('file') 
 194                 if not video_key 
or not f_url
: 
 197                     'id': '%s_%s' % (video_id
, video_key
.get('part', i 
+ 1)), 
 199                     'upload_date': video_key
.get('upload_date'), 
 200                     'duration': int_or_none(f
.get('length')), 
 202                     'protocol': 'm3u8_native', 
 209                 'duration': int_or_none(video_data
.get('length')), 
 212                 info
['_type'] = 'multi_video' 
 213                 info
['entries'] = entries
 
 214             elif len(entries
) == 1: 
 215                 i 
= entries
[0].copy() 
 220             for s 
in video_data
.get('strm', []): 
 221                 s_url 
= s
.get('purl') 
 224                 stype 
= s
.get('stype') 
 226                     formats
.extend(self
._extract
_m
3u8_formats
( 
 227                         s_url
, channel_id
, 'mp4', m3u8_id
=stype
, fatal
=False)) 
 228                 elif stype 
== 'RTMP': 
 230                     label 
= s
.get('label') 
 232                         format_id
.append(label
) 
 234                         'format_id': '-'.join(format_id
), 
 236                         'tbr': int_or_none(s
.get('bps')), 
 237                         'height': int_or_none(s
.get('brt')), 
 241             self
._sort
_formats
(formats
) 
 245                 'title': self
._live
_title
(title
),