2 from __future__ 
import unicode_literals
 
   6 from .common 
import InfoExtractor
 
   7 from ..compat 
import compat_xpath
 
  18 class AfreecaTVIE(InfoExtractor
): 
  20     IE_DESC 
= 'afreecatv.com' 
  24                             (?:(?:live|afbbs|www)\.)?afreeca(?:tv)?\.com(?::\d+)? 
  26                                 /app/(?:index|read_ucc_bbs)\.cgi| 
  27                                 /player/[Pp]layer\.(?:swf|html) 
  29                             vod\.afreecatv\.com/PLAYER/STATION/ 
  33     _NETRC_MACHINE 
= 'afreecatv' 
  35         'url': 'http://live.afreecatv.com:8079/app/index.cgi?szType=read_ucc_bbs&szBjId=dailyapril&nStationNo=16711924&nBbsNo=18605867&nTitleNo=36164052&szSkin=', 
  36         'md5': 'f72c89fe7ecc14c1b5ce506c4996046e', 
  40             'title': '데일리 에이프릴 요정들의 시상식!', 
  41             'thumbnail': 're:^https?://(?:video|st)img.afreecatv.com/.*$', 
  42             'uploader': 'dailyapril', 
  43             'uploader_id': 'dailyapril', 
  44             'upload_date': '20160503', 
  46         'skip': 'Video is gone', 
  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', 
  74         'skip': 'Video is gone', 
  76         'url': 'http://vod.afreecatv.com/PLAYER/STATION/18650793', 
  80             'title': '오늘은 다르다! 쏘님의 우월한 위아래~ 댄스리액션!', 
  81             'thumbnail': r
're:^https?://.*\.jpg$', 
  83             'uploader_id': 'badkids', 
  87             'skip_download': True, 
  90         'url': 'http://vod.afreecatv.com/PLAYER/STATION/10481652', 
  93             'title': "BJ유트루와 함께하는 '팅커벨 메이크업!'", 
  94             'thumbnail': 're:^https?://(?:video|st)img.afreecatv.com/.*$', 
  95             'uploader': 'dailyapril', 
  96             'uploader_id': 'dailyapril', 
 101             'md5': 'd8b7c174568da61d774ef0203159bf97', 
 103                 'id': '20160502_c4c62b9d_174361386_1', 
 105                 'title': "BJ유트루와 함께하는 '팅커벨 메이크업!' (part 1)", 
 106                 'thumbnail': 're:^https?://(?:video|st)img.afreecatv.com/.*$', 
 107                 'uploader': 'dailyapril', 
 108                 'uploader_id': 'dailyapril', 
 109                 'upload_date': '20160502', 
 113             'md5': '58f2ce7f6044e34439ab2d50612ab02b', 
 115                 'id': '20160502_39e739bb_174361386_2', 
 117                 'title': "BJ유트루와 함께하는 '팅커벨 메이크업!' (part 2)", 
 118                 'thumbnail': 're:^https?://(?:video|st)img.afreecatv.com/.*$', 
 119                 'uploader': 'dailyapril', 
 120                 'uploader_id': 'dailyapril', 
 121                 'upload_date': '20160502', 
 126             'skip_download': True, 
 130         'url': 'http://vod.afreecatv.com/PLAYER/STATION/20515605', 
 132             'id': '20170411_BE689A0E_190960999_1_2_h', 
 135             'thumbnail': 're:^https?://(?:video|st)img.afreecatv.com/.*$', 
 137             'uploader_id': 'dasl8121', 
 138             'upload_date': '20170411', 
 142             'skip_download': True, 
 146         'url': 'http://vod.afreecatv.com/PLAYER/STATION/32028439', 
 148             'id': '20180327_27901457_202289533_1', 
 150             'title': '[생]빨개요♥ (part 1)', 
 151             'thumbnail': 're:^https?://(?:video|st)img.afreecatv.com/.*$', 
 152             'uploader': '[SA]서아', 
 153             'uploader_id': 'bjdyrksu', 
 154             'upload_date': '20180327', 
 158             'skip_download': True, 
 160         'expected_warnings': ['adult content'], 
 162         'url': 'http://www.afreecatv.com/player/Player.swf?szType=szBjId=djleegoon&nStationNo=11273158&nBbsNo=13161095&nTitleNo=36327652', 
 163         'only_matching': True, 
 165         'url': 'http://vod.afreecatv.com/PLAYER/STATION/15055030', 
 166         'only_matching': True, 
 170     def parse_video_key(key
): 
 172         m 
= re
.match(r
'^(?P<upload_date>\d{8})_\w+_(?P<part>\d+)$', key
) 
 174             video_key
['upload_date'] = m
.group('upload_date') 
 175             video_key
['part'] = int(m
.group('part')) 
 178     def _real_initialize(self
): 
 182         username
, password 
= self
._get
_login
_info
() 
 190             'szPassword': password
, 
 192             'szScriptVar': 'oLoginRet', 
 196         response 
= self
._download
_json
( 
 197             'https://login.afreecatv.com/app/LoginAction.php', None, 
 198             'Logging in', data
=urlencode_postdata(login_form
)) 
 201             -4: 'Your account has been suspended due to a violation of our terms and policies.', 
 202             -5: 'https://member.afreecatv.com/app/user_delete_progress.php', 
 203             -6: 'https://login.afreecatv.com/membership/changeMember.php', 
 204             -8: "Hello! AfreecaTV here.\nThe username you have entered belongs to \n an account that requires a legal guardian's consent. \nIf you wish to use our services without restriction, \nplease make sure to go through the necessary verification process.", 
 205             -9: 'https://member.afreecatv.com/app/pop_login_block.php', 
 206             -11: 'https://login.afreecatv.com/afreeca/second_login.php', 
 207             -12: 'https://member.afreecatv.com/app/user_security.php', 
 208             0: 'The username does not exist or you have entered the wrong password.', 
 209             -1: 'The username does not exist or you have entered the wrong password.', 
 210             -3: 'You have entered your username/password incorrectly.', 
 211             -7: 'You cannot use your Global AfreecaTV account to access Korean AfreecaTV.', 
 212             -10: 'Sorry for the inconvenience. \nYour account has been blocked due to an unauthorized access. \nPlease contact our Help Center for assistance.', 
 213             -32008: 'You have failed to log in. Please contact our Help Center.', 
 216         result 
= int_or_none(response
.get('RESULT')) 
 218             error 
= _ERRORS
.get(result
, 'You have failed to log in.') 
 219             raise ExtractorError( 
 220                 'Unable to login: %s said: %s' % (self
.IE_NAME
, error
), 
 223     def _real_extract(self
, url
): 
 224         video_id 
= self
._match
_id
(url
) 
 226         webpage 
= self
._download
_webpage
(url
, video_id
) 
 228         if re
.search(r
'alert\(["\']This video has been deleted
', webpage): 
 229             raise ExtractorError( 
 230                 'Video 
%s has been deleted
' % video_id, expected=True) 
 232         station_id = self._search_regex( 
 233             r'nStationNo\s
*=\s
*(\d
+)', webpage, 'station
') 
 234         bbs_id = self._search_regex( 
 235             r'nBbsNo\s
*=\s
*(\d
+)', webpage, 'bbs
') 
 236         video_id = self._search_regex( 
 237             r'nTitleNo\s
*=\s
*(\d
+)', webpage, 'title
', default=video_id) 
 242                 'nTitleNo
': video_id, 
 243                 'nStationNo
': station_id, 
 247                 query['partialView
'] = 'SKIP_ADULT
' 
 248             video_xml = self._download_xml( 
 249                 'http
://afbbs
.afreecatv
.com
:8080/api
/video
/get_video_info
.php
', 
 250                 video_id, 'Downloading video info XML
%s' 
 251                 % (' (skipping adult
)' if partial_view else ''), 
 256             flag = xpath_text(video_xml, './track
/flag
', 'flag
', default=None) 
 257             if flag and flag == 'SUCCEED
': 
 259             if flag == 'PARTIAL_ADULT
': 
 260                 self._downloader.report_warning( 
 261                     'In accordance 
with local laws 
and regulations
, underage users are restricted 
from watching adult content
. ' 
 262                     'Only content suitable 
for all ages will be downloaded
. ' 
 263                     'Provide account credentials 
if you wish to download restricted content
.') 
 266             elif flag == 'ADULT
': 
 267                 error = 'Only users older than 
19 are able to watch this video
. Provide account credentials to download this content
.' 
 270             raise ExtractorError( 
 271                 '%s said
: %s' % (self.IE_NAME, error), expected=True) 
 273             raise ExtractorError('Unable to download video info
') 
 275         video_element = video_xml.findall(compat_xpath('./track
/video
'))[-1] 
 276         if video_element is None or video_element.text is None: 
 277             raise ExtractorError( 
 278                 'Video 
%s video does 
not exist
' % video_id, expected=True) 
 280         video_url = video_element.text.strip() 
 282         title = xpath_text(video_xml, './track
/title
', 'title
', fatal=True) 
 284         uploader = xpath_text(video_xml, './track
/nickname
', 'uploader
') 
 285         uploader_id = xpath_text(video_xml, './track
/bj_id
', 'uploader 
id') 
 286         duration = int_or_none(xpath_text( 
 287             video_xml, './track
/duration
', 'duration
')) 
 288         thumbnail = xpath_text(video_xml, './track
/titleImage
', 'thumbnail
') 
 291             'uploader
': uploader, 
 292             'uploader_id
': uploader_id, 
 293             'thumbnail
': thumbnail, 
 296         info = common_entry.copy() 
 300             'duration
': duration, 
 305             file_elements = video_element.findall(compat_xpath('./file')) 
 306             one = len(file_elements) == 1 
 307             for file_num, file_element in enumerate(file_elements, start=1): 
 308                 file_url = url_or_none(file_element.text) 
 311                 key = file_element.get('key
', '') 
 312                 upload_date = self._search_regex( 
 313                     r'^
(\d{8}
)_
', key, 'upload date
', default=None) 
 314                 file_duration = int_or_none(file_element.get('duration
')) 
 315                 format_id = key if key else '%s_%s' % (video_id, file_num) 
 316                 if determine_ext(file_url) == 'm3u8
': 
 317                     formats = self._extract_m3u8_formats( 
 318                         file_url, video_id, 'mp4
', entry_protocol='m3u8_native
', 
 320                         note='Downloading part 
%d m3u8 information
' % file_num) 
 328                 self._sort_formats(formats) 
 329                 file_info = common_entry.copy() 
 332                     'title
': title if one else '%s (part 
%d)' % (title, file_num), 
 333                     'upload_date
': upload_date, 
 334                     'duration
': file_duration, 
 337                 entries.append(file_info) 
 338             entries_info = info.copy() 
 339             entries_info.update({ 
 340                 '_type
': 'multi_video
', 
 348             'uploader
': uploader, 
 349             'uploader_id
': uploader_id, 
 350             'duration
': duration, 
 351             'thumbnail
': thumbnail, 
 354         if determine_ext(video_url) == 'm3u8
': 
 355             info['formats
'] = self._extract_m3u8_formats( 
 356                 video_url, video_id, 'mp4
', entry_protocol='m3u8_native
', 
 359             app, playpath = video_url.split('mp4
:') 
 363                 'play_path
': 'mp4
:' + playpath, 
 364                 'rtmp_live
': True,  # downloading won't end without this