2 from __future__ 
import unicode_literals
 
   8 from .common 
import InfoExtractor
 
  24 class NocoIE(InfoExtractor
): 
  25     _VALID_URL 
= r
'https?://(?:(?:www\.)?noco\.tv/emission/|player\.noco\.tv/\?idvideo=)(?P<id>\d+)' 
  26     _LOGIN_URL 
= 'http://noco.tv/do.php' 
  27     _API_URL_TEMPLATE 
= 'https://api.noco.tv/1.1/%s?ts=%s&tk=%s' 
  28     _SUB_LANG_TEMPLATE 
= '&sub_lang=%s' 
  29     _NETRC_MACHINE 
= 'noco' 
  33             'url': 'http://noco.tv/emission/11538/nolife/ami-ami-idol-hello-france/', 
  34             'md5': '0a993f0058ddbcd902630b2047ef710e', 
  38                 'title': 'Ami Ami Idol - Hello! France', 
  39                 'description': 'md5:4eaab46ab68fa4197a317a88a53d3b86', 
  40                 'upload_date': '20140412', 
  45             'skip': 'Requires noco account', 
  48             'url': 'http://noco.tv/emission/12610/lbl42/the-guild/s01e01-wake-up-call', 
  49             'md5': 'c190f1f48e313c55838f1f412225934d', 
  53                 'title': 'The Guild #1 - Wake-Up Call', 
  54                 'timestamp': 1403863200, 
  55                 'upload_date': '20140627', 
  60             'skip': 'Requires noco account', 
  64     def _real_initialize(self
): 
  68         (username
, password
) = self
._get
_login
_info
() 
  78         request 
= sanitized_Request(self
._LOGIN
_URL
, urlencode_postdata(login_form
)) 
  79         request
.add_header('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8') 
  81         login 
= self
._download
_json
(request
, None, 'Logging in as %s' % username
) 
  84             raise ExtractorError('Unable to login: %s' % clean_html(login
['erreur']), expected
=True) 
  88         return int(time
.time() * 1000) 
  90     def _call_api(self
, path
, video_id
, note
, sub_lang
=None): 
  91         ts 
= compat_str(self
._ts
() + self
._ts
_offset
) 
  92         tk 
= hashlib
.md5((hashlib
.md5(ts
.encode('ascii')).hexdigest() + '#8S?uCraTedap6a').encode('ascii')).hexdigest() 
  93         url 
= self
._API
_URL
_TEMPLATE 
% (path
, ts
, tk
) 
  95             url 
+= self
._SUB
_LANG
_TEMPLATE 
% sub_lang
 
  97         request 
= sanitized_Request(url
) 
  98         request
.add_header('Referer', self
._referer
) 
 100         resp 
= self
._download
_json
(request
, video_id
, note
) 
 102         if isinstance(resp
, dict) and resp
.get('error'): 
 103             self
._raise
_error
(resp
['error'], resp
['description']) 
 107     def _raise_error(self
, error
, description
): 
 108         raise ExtractorError( 
 109             '%s returned error: %s - %s' % (self
.IE_NAME
, error
, description
), 
 112     def _real_extract(self
, url
): 
 113         video_id 
= self
._match
_id
(url
) 
 115         # Timestamp adjustment offset between server time and local time 
 116         # must be calculated in order to use timestamps closest to server's 
 117         # in all API requests (see https://github.com/rg3/youtube-dl/issues/7864) 
 118         webpage 
= self
._download
_webpage
(url
, video_id
) 
 120         player_url 
= self
._search
_regex
( 
 121             r
'(["\'])(?P
<player
>https?
://noco\
.tv
/(?
:[^
/]+/)+NocoPlayer
.+?\
.swf
.*?
)\
1', 
 122             webpage, 'noco player
', group='player
', 
 123             default='http
://noco
.tv
/cdata
/js
/player
/NocoPlayer
-v1
.2
.40.swf
') 
 125         qs = compat_urlparse.parse_qs(compat_urlparse.urlparse(player_url).query) 
 126         ts = int_or_none(qs.get('ts
', [None])[0]) 
 127         self._ts_offset = ts - self._ts() if ts else 0 
 128         self._referer = player_url 
 130         medias = self._call_api( 
 131             'shows
/%s/medias
' % video_id, 
 132             video_id, 'Downloading video JSON
') 
 134         show = self._call_api( 
 135             'shows
/by_id
/%s' % video_id, 
 136             video_id, 'Downloading show JSON
')[0] 
 138         options = self._call_api( 
 139             'users
/init
', video_id, 
 140             'Downloading user options JSON
')['options
'] 
 141         audio_lang_pref = options.get('audio_language
') or options.get('language
', 'fr
') 
 143         if audio_lang_pref == 'original
': 
 144             audio_lang_pref = show['original_lang
'] 
 146             audio_lang_pref = list(medias.keys())[0] 
 147         elif audio_lang_pref not in medias: 
 148             audio_lang_pref = 'fr
' 
 150         qualities = self._call_api( 
 152             video_id, 'Downloading qualities JSON
') 
 156         for audio_lang, audio_lang_dict in medias.items(): 
 157             preference = 1 if audio_lang == audio_lang_pref else 0 
 158             for sub_lang, lang_dict in audio_lang_dict['video_list
'].items(): 
 159                 for format_id, fmt in lang_dict['quality_list
'].items(): 
 160                     format_id_extended = 'audio
-%s_sub
-%s_%s' % (audio_lang, sub_lang, format_id) 
 162                     video = self._call_api( 
 163                         'shows
/%s/video
/%s/%s' % (video_id, format_id.lower(), audio_lang), 
 164                         video_id, 'Downloading 
%s video JSON
' % format_id_extended, 
 165                         sub_lang if sub_lang != 'none
' else None) 
 167                     file_url = video['file'] 
 171                     if file_url in ['forbidden
', 'not found
']: 
 172                         popmessage = video['popmessage
'] 
 173                         self._raise_error(popmessage['title
'], popmessage['message
']) 
 177                         'format_id
': format_id_extended, 
 178                         'width
': int_or_none(fmt.get('res_width
')), 
 179                         'height
': int_or_none(fmt.get('res_lines
')), 
 180                         'abr
': int_or_none(fmt.get('audiobitrate
'), 1000), 
 181                         'vbr
': int_or_none(fmt.get('videobitrate
'), 1000), 
 182                         'filesize
': int_or_none(fmt.get('filesize
')), 
 183                         'format_note
': qualities[format_id].get('quality_name
'), 
 184                         'quality
': qualities[format_id].get('priority
'), 
 185                         'preference
': preference, 
 188         self._sort_formats(formats) 
 190         timestamp = parse_iso8601(show.get('online_date_start_utc
'), ' ') 
 192         if timestamp is not None and timestamp < 0: 
 195         uploader = show.get('partner_name
') 
 196         uploader_id = show.get('partner_key
') 
 197         duration = float_or_none(show.get('duration_ms
'), 1000) 
 200         for thumbnail_key, thumbnail_url in show.items(): 
 201             m = re.search(r'^
screenshot_(?P
<width
>\d
+)x(?P
<height
>\d
+)$
', thumbnail_key) 
 205                 'url
': thumbnail_url, 
 206                 'width
': int(m.group('width
')), 
 207                 'height
': int(m.group('height
')), 
 210         episode = show.get('show_TT
') or show.get('show_OT
') 
 211         family = show.get('family_TT
') or show.get('family_OT
') 
 212         episode_number = show.get('episode_number
') 
 218             title += ' #' + compat_str(episode_number) 
 220             title 
+= ' - ' + compat_str(episode
) 
 222         description 
= show
.get('show_resume') or show
.get('family_resume') 
 227             'description': description
, 
 228             'thumbnails': thumbnails
, 
 229             'timestamp': timestamp
, 
 230             'uploader': uploader
, 
 231             'uploader_id': uploader_id
, 
 232             'duration': duration
,