]>
jfr.im git - yt-dlp.git/blob - youtube_dl/extractor/noco.py
2 from __future__
import unicode_literals
8 from .common
import InfoExtractor
12 compat_urllib_request
,
23 class NocoIE(InfoExtractor
):
24 _VALID_URL
= r
'http://(?:(?:www\.)?noco\.tv/emission/|player\.noco\.tv/\?idvideo=)(?P<id>\d+)'
25 _LOGIN_URL
= 'http://noco.tv/do.php'
26 _API_URL_TEMPLATE
= 'https://api.noco.tv/1.1/%s?ts=%s&tk=%s'
27 _SUB_LANG_TEMPLATE
= '&sub_lang=%s'
28 _NETRC_MACHINE
= 'noco'
32 'url': 'http://noco.tv/emission/11538/nolife/ami-ami-idol-hello-france/',
33 'md5': '0a993f0058ddbcd902630b2047ef710e',
37 'title': 'Ami Ami Idol - Hello! France',
38 'description': 'md5:4eaab46ab68fa4197a317a88a53d3b86',
39 'upload_date': '20140412',
44 'skip': 'Requires noco account',
47 'url': 'http://noco.tv/emission/12610/lbl42/the-guild/s01e01-wake-up-call',
48 'md5': 'c190f1f48e313c55838f1f412225934d',
52 'title': 'The Guild #1 - Wake-Up Call',
53 'timestamp': 1403863200,
54 'upload_date': '20140627',
59 'skip': 'Requires noco account',
63 def _real_initialize(self
):
67 (username
, password
) = self
._get
_login
_info
()
77 request
= compat_urllib_request
.Request(self
._LOGIN
_URL
, compat_urllib_parse
.urlencode(login_form
))
78 request
.add_header('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8')
80 login
= self
._download
_json
(request
, None, 'Logging in as %s' % username
)
83 raise ExtractorError('Unable to login: %s' % clean_html(login
['erreur']), expected
=True)
85 def _call_api(self
, path
, video_id
, note
, sub_lang
=None):
86 ts
= compat_str(int(time
.time() * 1000))
87 tk
= hashlib
.md5((hashlib
.md5(ts
.encode('ascii')).hexdigest() + '#8S?uCraTedap6a').encode('ascii')).hexdigest()
88 url
= self
._API
_URL
_TEMPLATE
% (path
, ts
, tk
)
90 url
+= self
._SUB
_LANG
_TEMPLATE
% sub_lang
92 resp
= self
._download
_json
(url
, video_id
, note
)
94 if isinstance(resp
, dict) and resp
.get('error'):
95 self
._raise
_error
(resp
['error'], resp
['description'])
99 def _raise_error(self
, error
, description
):
100 raise ExtractorError(
101 '%s returned error: %s - %s' % (self
.IE_NAME
, error
, description
),
104 def _real_extract(self
, url
):
105 mobj
= re
.match(self
._VALID
_URL
, url
)
106 video_id
= mobj
.group('id')
108 medias
= self
._call
_api
(
109 'shows/%s/medias' % video_id
,
110 video_id
, 'Downloading video JSON')
112 show
= self
._call
_api
(
113 'shows/by_id/%s' % video_id
,
114 video_id
, 'Downloading show JSON')[0]
116 options
= self
._call
_api
(
117 'users/init', video_id
,
118 'Downloading user options JSON')['options']
119 audio_lang_pref
= options
.get('audio_language') or options
.get('language', 'fr')
121 if audio_lang_pref
== 'original':
122 audio_lang_pref
= show
['original_lang']
124 audio_lang_pref
= list(medias
.keys())[0]
125 elif audio_lang_pref
not in medias
:
126 audio_lang_pref
= 'fr'
128 qualities
= self
._call
_api
(
130 video_id
, 'Downloading qualities JSON')
134 for audio_lang
, audio_lang_dict
in medias
.items():
135 preference
= 1 if audio_lang
== audio_lang_pref
else 0
136 for sub_lang
, lang_dict
in audio_lang_dict
['video_list'].items():
137 for format_id
, fmt
in lang_dict
['quality_list'].items():
138 format_id_extended
= 'audio-%s_sub-%s_%s' % (audio_lang
, sub_lang
, format_id
)
140 video
= self
._call
_api
(
141 'shows/%s/video/%s/%s' % (video_id
, format_id
.lower(), audio_lang
),
142 video_id
, 'Downloading %s video JSON' % format_id_extended
,
143 sub_lang
if sub_lang
!= 'none' else None)
145 file_url
= video
['file']
149 if file_url
in ['forbidden', 'not found']:
150 popmessage
= video
['popmessage']
151 self
._raise
_error
(popmessage
['title'], popmessage
['message'])
155 'format_id': format_id_extended
,
156 'width': int_or_none(fmt
.get('res_width')),
157 'height': int_or_none(fmt
.get('res_lines')),
158 'abr': int_or_none(fmt
.get('audiobitrate')),
159 'vbr': int_or_none(fmt
.get('videobitrate')),
160 'filesize': int_or_none(fmt
.get('filesize')),
161 'format_note': qualities
[format_id
].get('quality_name'),
162 'quality': qualities
[format_id
].get('priority'),
163 'preference': preference
,
166 self
._sort
_formats
(formats
)
168 timestamp
= parse_iso8601(show
.get('online_date_start_utc'), ' ')
170 if timestamp
is not None and timestamp
< 0:
173 uploader
= show
.get('partner_name')
174 uploader_id
= show
.get('partner_key')
175 duration
= float_or_none(show
.get('duration_ms'), 1000)
178 for thumbnail_key
, thumbnail_url
in show
.items():
179 m
= re
.search(r
'^screenshot_(?P<width>\d+)x(?P<height>\d+)$', thumbnail_key
)
183 'url': thumbnail_url
,
184 'width': int(m
.group('width')),
185 'height': int(m
.group('height')),
188 episode
= show
.get('show_TT') or show
.get('show_OT')
189 family
= show
.get('family_TT') or show
.get('family_OT')
190 episode_number
= show
.get('episode_number')
196 title
+= ' #' + compat_str(episode_number
)
198 title
+= ' - ' + compat_str(episode
)
200 description
= show
.get('show_resume') or show
.get('family_resume')
205 'description': description
,
206 'thumbnails': thumbnails
,
207 'timestamp': timestamp
,
208 'uploader': uploader
,
209 'uploader_id': uploader_id
,
210 'duration': duration
,