]> Raphaƫl G. Git Repositories - youtubedl/blob - test/test_YoutubeDL.py
Update changelog.
[youtubedl] / test / test_YoutubeDL.py
1 #!/usr/bin/env python
2
3 from __future__ import unicode_literals
4
5 # Allow direct execution
6 import os
7 import sys
8 import unittest
9 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
10
11 import copy
12
13 from test.helper import FakeYDL, assertRegexpMatches
14 from youtube_dl import YoutubeDL
15 from youtube_dl.extractor import YoutubeIE
16 from youtube_dl.postprocessor.common import PostProcessor
17 from youtube_dl.utils import match_filter_func
18
19 TEST_URL = 'http://localhost/sample.mp4'
20
21
22 class YDL(FakeYDL):
23 def __init__(self, *args, **kwargs):
24 super(YDL, self).__init__(*args, **kwargs)
25 self.downloaded_info_dicts = []
26 self.msgs = []
27
28 def process_info(self, info_dict):
29 self.downloaded_info_dicts.append(info_dict)
30
31 def to_screen(self, msg):
32 self.msgs.append(msg)
33
34
35 def _make_result(formats, **kwargs):
36 res = {
37 'formats': formats,
38 'id': 'testid',
39 'title': 'testttitle',
40 'extractor': 'testex',
41 }
42 res.update(**kwargs)
43 return res
44
45
46 class TestFormatSelection(unittest.TestCase):
47 def test_prefer_free_formats(self):
48 # Same resolution => download webm
49 ydl = YDL()
50 ydl.params['prefer_free_formats'] = True
51 formats = [
52 {'ext': 'webm', 'height': 460, 'url': TEST_URL},
53 {'ext': 'mp4', 'height': 460, 'url': TEST_URL},
54 ]
55 info_dict = _make_result(formats)
56 yie = YoutubeIE(ydl)
57 yie._sort_formats(info_dict['formats'])
58 ydl.process_ie_result(info_dict)
59 downloaded = ydl.downloaded_info_dicts[0]
60 self.assertEqual(downloaded['ext'], 'webm')
61
62 # Different resolution => download best quality (mp4)
63 ydl = YDL()
64 ydl.params['prefer_free_formats'] = True
65 formats = [
66 {'ext': 'webm', 'height': 720, 'url': TEST_URL},
67 {'ext': 'mp4', 'height': 1080, 'url': TEST_URL},
68 ]
69 info_dict['formats'] = formats
70 yie = YoutubeIE(ydl)
71 yie._sort_formats(info_dict['formats'])
72 ydl.process_ie_result(info_dict)
73 downloaded = ydl.downloaded_info_dicts[0]
74 self.assertEqual(downloaded['ext'], 'mp4')
75
76 # No prefer_free_formats => prefer mp4 and flv for greater compatibility
77 ydl = YDL()
78 ydl.params['prefer_free_formats'] = False
79 formats = [
80 {'ext': 'webm', 'height': 720, 'url': TEST_URL},
81 {'ext': 'mp4', 'height': 720, 'url': TEST_URL},
82 {'ext': 'flv', 'height': 720, 'url': TEST_URL},
83 ]
84 info_dict['formats'] = formats
85 yie = YoutubeIE(ydl)
86 yie._sort_formats(info_dict['formats'])
87 ydl.process_ie_result(info_dict)
88 downloaded = ydl.downloaded_info_dicts[0]
89 self.assertEqual(downloaded['ext'], 'mp4')
90
91 ydl = YDL()
92 ydl.params['prefer_free_formats'] = False
93 formats = [
94 {'ext': 'flv', 'height': 720, 'url': TEST_URL},
95 {'ext': 'webm', 'height': 720, 'url': TEST_URL},
96 ]
97 info_dict['formats'] = formats
98 yie = YoutubeIE(ydl)
99 yie._sort_formats(info_dict['formats'])
100 ydl.process_ie_result(info_dict)
101 downloaded = ydl.downloaded_info_dicts[0]
102 self.assertEqual(downloaded['ext'], 'flv')
103
104 def test_format_selection(self):
105 formats = [
106 {'format_id': '35', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL},
107 {'format_id': '45', 'ext': 'webm', 'preference': 2, 'url': TEST_URL},
108 {'format_id': '47', 'ext': 'webm', 'preference': 3, 'url': TEST_URL},
109 {'format_id': '2', 'ext': 'flv', 'preference': 4, 'url': TEST_URL},
110 ]
111 info_dict = _make_result(formats)
112
113 ydl = YDL({'format': '20/47'})
114 ydl.process_ie_result(info_dict.copy())
115 downloaded = ydl.downloaded_info_dicts[0]
116 self.assertEqual(downloaded['format_id'], '47')
117
118 ydl = YDL({'format': '20/71/worst'})
119 ydl.process_ie_result(info_dict.copy())
120 downloaded = ydl.downloaded_info_dicts[0]
121 self.assertEqual(downloaded['format_id'], '35')
122
123 ydl = YDL()
124 ydl.process_ie_result(info_dict.copy())
125 downloaded = ydl.downloaded_info_dicts[0]
126 self.assertEqual(downloaded['format_id'], '2')
127
128 ydl = YDL({'format': 'webm/mp4'})
129 ydl.process_ie_result(info_dict.copy())
130 downloaded = ydl.downloaded_info_dicts[0]
131 self.assertEqual(downloaded['format_id'], '47')
132
133 ydl = YDL({'format': '3gp/40/mp4'})
134 ydl.process_ie_result(info_dict.copy())
135 downloaded = ydl.downloaded_info_dicts[0]
136 self.assertEqual(downloaded['format_id'], '35')
137
138 def test_format_selection_audio(self):
139 formats = [
140 {'format_id': 'audio-low', 'ext': 'webm', 'preference': 1, 'vcodec': 'none', 'url': TEST_URL},
141 {'format_id': 'audio-mid', 'ext': 'webm', 'preference': 2, 'vcodec': 'none', 'url': TEST_URL},
142 {'format_id': 'audio-high', 'ext': 'flv', 'preference': 3, 'vcodec': 'none', 'url': TEST_URL},
143 {'format_id': 'vid', 'ext': 'mp4', 'preference': 4, 'url': TEST_URL},
144 ]
145 info_dict = _make_result(formats)
146
147 ydl = YDL({'format': 'bestaudio'})
148 ydl.process_ie_result(info_dict.copy())
149 downloaded = ydl.downloaded_info_dicts[0]
150 self.assertEqual(downloaded['format_id'], 'audio-high')
151
152 ydl = YDL({'format': 'worstaudio'})
153 ydl.process_ie_result(info_dict.copy())
154 downloaded = ydl.downloaded_info_dicts[0]
155 self.assertEqual(downloaded['format_id'], 'audio-low')
156
157 formats = [
158 {'format_id': 'vid-low', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL},
159 {'format_id': 'vid-high', 'ext': 'mp4', 'preference': 2, 'url': TEST_URL},
160 ]
161 info_dict = _make_result(formats)
162
163 ydl = YDL({'format': 'bestaudio/worstaudio/best'})
164 ydl.process_ie_result(info_dict.copy())
165 downloaded = ydl.downloaded_info_dicts[0]
166 self.assertEqual(downloaded['format_id'], 'vid-high')
167
168 def test_format_selection_audio_exts(self):
169 formats = [
170 {'format_id': 'mp3-64', 'ext': 'mp3', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
171 {'format_id': 'ogg-64', 'ext': 'ogg', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
172 {'format_id': 'aac-64', 'ext': 'aac', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
173 {'format_id': 'mp3-32', 'ext': 'mp3', 'abr': 32, 'url': 'http://_', 'vcodec': 'none'},
174 {'format_id': 'aac-32', 'ext': 'aac', 'abr': 32, 'url': 'http://_', 'vcodec': 'none'},
175 ]
176
177 info_dict = _make_result(formats)
178 ydl = YDL({'format': 'best'})
179 ie = YoutubeIE(ydl)
180 ie._sort_formats(info_dict['formats'])
181 ydl.process_ie_result(copy.deepcopy(info_dict))
182 downloaded = ydl.downloaded_info_dicts[0]
183 self.assertEqual(downloaded['format_id'], 'aac-64')
184
185 ydl = YDL({'format': 'mp3'})
186 ie = YoutubeIE(ydl)
187 ie._sort_formats(info_dict['formats'])
188 ydl.process_ie_result(copy.deepcopy(info_dict))
189 downloaded = ydl.downloaded_info_dicts[0]
190 self.assertEqual(downloaded['format_id'], 'mp3-64')
191
192 ydl = YDL({'prefer_free_formats': True})
193 ie = YoutubeIE(ydl)
194 ie._sort_formats(info_dict['formats'])
195 ydl.process_ie_result(copy.deepcopy(info_dict))
196 downloaded = ydl.downloaded_info_dicts[0]
197 self.assertEqual(downloaded['format_id'], 'ogg-64')
198
199 def test_format_selection_video(self):
200 formats = [
201 {'format_id': 'dash-video-low', 'ext': 'mp4', 'preference': 1, 'acodec': 'none', 'url': TEST_URL},
202 {'format_id': 'dash-video-high', 'ext': 'mp4', 'preference': 2, 'acodec': 'none', 'url': TEST_URL},
203 {'format_id': 'vid', 'ext': 'mp4', 'preference': 3, 'url': TEST_URL},
204 ]
205 info_dict = _make_result(formats)
206
207 ydl = YDL({'format': 'bestvideo'})
208 ydl.process_ie_result(info_dict.copy())
209 downloaded = ydl.downloaded_info_dicts[0]
210 self.assertEqual(downloaded['format_id'], 'dash-video-high')
211
212 ydl = YDL({'format': 'worstvideo'})
213 ydl.process_ie_result(info_dict.copy())
214 downloaded = ydl.downloaded_info_dicts[0]
215 self.assertEqual(downloaded['format_id'], 'dash-video-low')
216
217 def test_youtube_format_selection(self):
218 order = [
219 '38', '37', '46', '22', '45', '35', '44', '18', '34', '43', '6', '5', '36', '17', '13',
220 # Apple HTTP Live Streaming
221 '96', '95', '94', '93', '92', '132', '151',
222 # 3D
223 '85', '84', '102', '83', '101', '82', '100',
224 # Dash video
225 '137', '248', '136', '247', '135', '246',
226 '245', '244', '134', '243', '133', '242', '160',
227 # Dash audio
228 '141', '172', '140', '171', '139',
229 ]
230
231 for f1id, f2id in zip(order, order[1:]):
232 f1 = YoutubeIE._formats[f1id].copy()
233 f1['format_id'] = f1id
234 f1['url'] = 'url:' + f1id
235 f2 = YoutubeIE._formats[f2id].copy()
236 f2['format_id'] = f2id
237 f2['url'] = 'url:' + f2id
238
239 info_dict = _make_result([f1, f2], extractor='youtube')
240 ydl = YDL({'format': 'best/bestvideo'})
241 yie = YoutubeIE(ydl)
242 yie._sort_formats(info_dict['formats'])
243 ydl.process_ie_result(info_dict)
244 downloaded = ydl.downloaded_info_dicts[0]
245 self.assertEqual(downloaded['format_id'], f1id)
246
247 info_dict = _make_result([f2, f1], extractor='youtube')
248 ydl = YDL({'format': 'best/bestvideo'})
249 yie = YoutubeIE(ydl)
250 yie._sort_formats(info_dict['formats'])
251 ydl.process_ie_result(info_dict)
252 downloaded = ydl.downloaded_info_dicts[0]
253 self.assertEqual(downloaded['format_id'], f1id)
254
255 def test_format_filtering(self):
256 formats = [
257 {'format_id': 'A', 'filesize': 500, 'width': 1000},
258 {'format_id': 'B', 'filesize': 1000, 'width': 500},
259 {'format_id': 'C', 'filesize': 1000, 'width': 400},
260 {'format_id': 'D', 'filesize': 2000, 'width': 600},
261 {'format_id': 'E', 'filesize': 3000},
262 {'format_id': 'F'},
263 {'format_id': 'G', 'filesize': 1000000},
264 ]
265 for f in formats:
266 f['url'] = 'http://_/'
267 f['ext'] = 'unknown'
268 info_dict = _make_result(formats)
269
270 ydl = YDL({'format': 'best[filesize<3000]'})
271 ydl.process_ie_result(info_dict)
272 downloaded = ydl.downloaded_info_dicts[0]
273 self.assertEqual(downloaded['format_id'], 'D')
274
275 ydl = YDL({'format': 'best[filesize<=3000]'})
276 ydl.process_ie_result(info_dict)
277 downloaded = ydl.downloaded_info_dicts[0]
278 self.assertEqual(downloaded['format_id'], 'E')
279
280 ydl = YDL({'format': 'best[filesize <= ? 3000]'})
281 ydl.process_ie_result(info_dict)
282 downloaded = ydl.downloaded_info_dicts[0]
283 self.assertEqual(downloaded['format_id'], 'F')
284
285 ydl = YDL({'format': 'best [filesize = 1000] [width>450]'})
286 ydl.process_ie_result(info_dict)
287 downloaded = ydl.downloaded_info_dicts[0]
288 self.assertEqual(downloaded['format_id'], 'B')
289
290 ydl = YDL({'format': 'best [filesize = 1000] [width!=450]'})
291 ydl.process_ie_result(info_dict)
292 downloaded = ydl.downloaded_info_dicts[0]
293 self.assertEqual(downloaded['format_id'], 'C')
294
295 ydl = YDL({'format': '[filesize>?1]'})
296 ydl.process_ie_result(info_dict)
297 downloaded = ydl.downloaded_info_dicts[0]
298 self.assertEqual(downloaded['format_id'], 'G')
299
300 ydl = YDL({'format': '[filesize<1M]'})
301 ydl.process_ie_result(info_dict)
302 downloaded = ydl.downloaded_info_dicts[0]
303 self.assertEqual(downloaded['format_id'], 'E')
304
305 ydl = YDL({'format': '[filesize<1MiB]'})
306 ydl.process_ie_result(info_dict)
307 downloaded = ydl.downloaded_info_dicts[0]
308 self.assertEqual(downloaded['format_id'], 'G')
309
310
311 class TestYoutubeDL(unittest.TestCase):
312 def test_subtitles(self):
313 def s_formats(lang, autocaption=False):
314 return [{
315 'ext': ext,
316 'url': 'http://localhost/video.%s.%s' % (lang, ext),
317 '_auto': autocaption,
318 } for ext in ['vtt', 'srt', 'ass']]
319 subtitles = dict((l, s_formats(l)) for l in ['en', 'fr', 'es'])
320 auto_captions = dict((l, s_formats(l, True)) for l in ['it', 'pt', 'es'])
321 info_dict = {
322 'id': 'test',
323 'title': 'Test',
324 'url': 'http://localhost/video.mp4',
325 'subtitles': subtitles,
326 'automatic_captions': auto_captions,
327 'extractor': 'TEST',
328 }
329
330 def get_info(params={}):
331 params.setdefault('simulate', True)
332 ydl = YDL(params)
333 ydl.report_warning = lambda *args, **kargs: None
334 return ydl.process_video_result(info_dict, download=False)
335
336 result = get_info()
337 self.assertFalse(result.get('requested_subtitles'))
338 self.assertEqual(result['subtitles'], subtitles)
339 self.assertEqual(result['automatic_captions'], auto_captions)
340
341 result = get_info({'writesubtitles': True})
342 subs = result['requested_subtitles']
343 self.assertTrue(subs)
344 self.assertEqual(set(subs.keys()), set(['en']))
345 self.assertTrue(subs['en'].get('data') is None)
346 self.assertEqual(subs['en']['ext'], 'ass')
347
348 result = get_info({'writesubtitles': True, 'subtitlesformat': 'foo/srt'})
349 subs = result['requested_subtitles']
350 self.assertEqual(subs['en']['ext'], 'srt')
351
352 result = get_info({'writesubtitles': True, 'subtitleslangs': ['es', 'fr', 'it']})
353 subs = result['requested_subtitles']
354 self.assertTrue(subs)
355 self.assertEqual(set(subs.keys()), set(['es', 'fr']))
356
357 result = get_info({'writesubtitles': True, 'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
358 subs = result['requested_subtitles']
359 self.assertTrue(subs)
360 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
361 self.assertFalse(subs['es']['_auto'])
362 self.assertTrue(subs['pt']['_auto'])
363
364 result = get_info({'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
365 subs = result['requested_subtitles']
366 self.assertTrue(subs)
367 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
368 self.assertTrue(subs['es']['_auto'])
369 self.assertTrue(subs['pt']['_auto'])
370
371 def test_add_extra_info(self):
372 test_dict = {
373 'extractor': 'Foo',
374 }
375 extra_info = {
376 'extractor': 'Bar',
377 'playlist': 'funny videos',
378 }
379 YDL.add_extra_info(test_dict, extra_info)
380 self.assertEqual(test_dict['extractor'], 'Foo')
381 self.assertEqual(test_dict['playlist'], 'funny videos')
382
383 def test_prepare_filename(self):
384 info = {
385 'id': '1234',
386 'ext': 'mp4',
387 'width': None,
388 }
389
390 def fname(templ):
391 ydl = YoutubeDL({'outtmpl': templ})
392 return ydl.prepare_filename(info)
393 self.assertEqual(fname('%(id)s.%(ext)s'), '1234.mp4')
394 self.assertEqual(fname('%(id)s-%(width)s.%(ext)s'), '1234-NA.mp4')
395 # Replace missing fields with 'NA'
396 self.assertEqual(fname('%(uploader_date)s-%(id)s.%(ext)s'), 'NA-1234.mp4')
397
398 def test_format_note(self):
399 ydl = YoutubeDL()
400 self.assertEqual(ydl._format_note({}), '')
401 assertRegexpMatches(self, ydl._format_note({
402 'vbr': 10,
403 }), '^\s*10k$')
404
405 def test_postprocessors(self):
406 filename = 'post-processor-testfile.mp4'
407 audiofile = filename + '.mp3'
408
409 class SimplePP(PostProcessor):
410 def run(self, info):
411 with open(audiofile, 'wt') as f:
412 f.write('EXAMPLE')
413 return [info['filepath']], info
414
415 def run_pp(params, PP):
416 with open(filename, 'wt') as f:
417 f.write('EXAMPLE')
418 ydl = YoutubeDL(params)
419 ydl.add_post_processor(PP())
420 ydl.post_process(filename, {'filepath': filename})
421
422 run_pp({'keepvideo': True}, SimplePP)
423 self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
424 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
425 os.unlink(filename)
426 os.unlink(audiofile)
427
428 run_pp({'keepvideo': False}, SimplePP)
429 self.assertFalse(os.path.exists(filename), '%s exists' % filename)
430 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
431 os.unlink(audiofile)
432
433 class ModifierPP(PostProcessor):
434 def run(self, info):
435 with open(info['filepath'], 'wt') as f:
436 f.write('MODIFIED')
437 return [], info
438
439 run_pp({'keepvideo': False}, ModifierPP)
440 self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
441 os.unlink(filename)
442
443 def test_match_filter(self):
444 class FilterYDL(YDL):
445 def __init__(self, *args, **kwargs):
446 super(FilterYDL, self).__init__(*args, **kwargs)
447 self.params['simulate'] = True
448
449 def process_info(self, info_dict):
450 super(YDL, self).process_info(info_dict)
451
452 def _match_entry(self, info_dict, incomplete):
453 res = super(FilterYDL, self)._match_entry(info_dict, incomplete)
454 if res is None:
455 self.downloaded_info_dicts.append(info_dict)
456 return res
457
458 first = {
459 'id': '1',
460 'url': TEST_URL,
461 'title': 'one',
462 'extractor': 'TEST',
463 'duration': 30,
464 'filesize': 10 * 1024,
465 }
466 second = {
467 'id': '2',
468 'url': TEST_URL,
469 'title': 'two',
470 'extractor': 'TEST',
471 'duration': 10,
472 'description': 'foo',
473 'filesize': 5 * 1024,
474 }
475 videos = [first, second]
476
477 def get_videos(filter_=None):
478 ydl = FilterYDL({'match_filter': filter_})
479 for v in videos:
480 ydl.process_ie_result(v, download=True)
481 return [v['id'] for v in ydl.downloaded_info_dicts]
482
483 res = get_videos()
484 self.assertEqual(res, ['1', '2'])
485
486 def f(v):
487 if v['id'] == '1':
488 return None
489 else:
490 return 'Video id is not 1'
491 res = get_videos(f)
492 self.assertEqual(res, ['1'])
493
494 f = match_filter_func('duration < 30')
495 res = get_videos(f)
496 self.assertEqual(res, ['2'])
497
498 f = match_filter_func('description = foo')
499 res = get_videos(f)
500 self.assertEqual(res, ['2'])
501
502 f = match_filter_func('description =? foo')
503 res = get_videos(f)
504 self.assertEqual(res, ['1', '2'])
505
506 f = match_filter_func('filesize > 5KiB')
507 res = get_videos(f)
508 self.assertEqual(res, ['1'])
509
510
511 if __name__ == '__main__':
512 unittest.main()