3 from __future__ import unicode_literals
5 # Allow direct execution
9 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
13 from test.helper import FakeYDL, assertRegexpMatches
14 from youtube_dl import YoutubeDL
15 from youtube_dl.compat import compat_str
16 from youtube_dl.extractor import YoutubeIE
17 from youtube_dl.postprocessor.common import PostProcessor
18 from youtube_dl.utils import ExtractorError, match_filter_func
20 TEST_URL = 'http://localhost/sample.mp4'
24 def __init__(self, *args, **kwargs):
25 super(YDL, self).__init__(*args, **kwargs)
26 self.downloaded_info_dicts = []
29 def process_info(self, info_dict):
30 self.downloaded_info_dicts.append(info_dict)
32 def to_screen(self, msg):
36 def _make_result(formats, **kwargs):
40 'title': 'testttitle',
41 'extractor': 'testex',
47 class TestFormatSelection(unittest.TestCase):
48 def test_prefer_free_formats(self):
49 # Same resolution => download webm
51 ydl.params['prefer_free_formats'] = True
53 {'ext': 'webm', 'height': 460, 'url': TEST_URL},
54 {'ext': 'mp4', 'height': 460, 'url': TEST_URL},
56 info_dict = _make_result(formats)
58 yie._sort_formats(info_dict['formats'])
59 ydl.process_ie_result(info_dict)
60 downloaded = ydl.downloaded_info_dicts[0]
61 self.assertEqual(downloaded['ext'], 'webm')
63 # Different resolution => download best quality (mp4)
65 ydl.params['prefer_free_formats'] = True
67 {'ext': 'webm', 'height': 720, 'url': TEST_URL},
68 {'ext': 'mp4', 'height': 1080, 'url': TEST_URL},
70 info_dict['formats'] = formats
72 yie._sort_formats(info_dict['formats'])
73 ydl.process_ie_result(info_dict)
74 downloaded = ydl.downloaded_info_dicts[0]
75 self.assertEqual(downloaded['ext'], 'mp4')
77 # No prefer_free_formats => prefer mp4 and flv for greater compatibility
79 ydl.params['prefer_free_formats'] = False
81 {'ext': 'webm', 'height': 720, 'url': TEST_URL},
82 {'ext': 'mp4', 'height': 720, 'url': TEST_URL},
83 {'ext': 'flv', 'height': 720, 'url': TEST_URL},
85 info_dict['formats'] = formats
87 yie._sort_formats(info_dict['formats'])
88 ydl.process_ie_result(info_dict)
89 downloaded = ydl.downloaded_info_dicts[0]
90 self.assertEqual(downloaded['ext'], 'mp4')
93 ydl.params['prefer_free_formats'] = False
95 {'ext': 'flv', 'height': 720, 'url': TEST_URL},
96 {'ext': 'webm', 'height': 720, 'url': TEST_URL},
98 info_dict['formats'] = formats
100 yie._sort_formats(info_dict['formats'])
101 ydl.process_ie_result(info_dict)
102 downloaded = ydl.downloaded_info_dicts[0]
103 self.assertEqual(downloaded['ext'], 'flv')
105 def test_format_selection(self):
107 {'format_id': '35', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL},
108 {'format_id': '45', 'ext': 'webm', 'preference': 2, 'url': TEST_URL},
109 {'format_id': '47', 'ext': 'webm', 'preference': 3, 'url': TEST_URL},
110 {'format_id': '2', 'ext': 'flv', 'preference': 4, 'url': TEST_URL},
112 info_dict = _make_result(formats)
114 ydl = YDL({'format': '20/47'})
115 ydl.process_ie_result(info_dict.copy())
116 downloaded = ydl.downloaded_info_dicts[0]
117 self.assertEqual(downloaded['format_id'], '47')
119 ydl = YDL({'format': '20/71/worst'})
120 ydl.process_ie_result(info_dict.copy())
121 downloaded = ydl.downloaded_info_dicts[0]
122 self.assertEqual(downloaded['format_id'], '35')
125 ydl.process_ie_result(info_dict.copy())
126 downloaded = ydl.downloaded_info_dicts[0]
127 self.assertEqual(downloaded['format_id'], '2')
129 ydl = YDL({'format': 'webm/mp4'})
130 ydl.process_ie_result(info_dict.copy())
131 downloaded = ydl.downloaded_info_dicts[0]
132 self.assertEqual(downloaded['format_id'], '47')
134 ydl = YDL({'format': '3gp/40/mp4'})
135 ydl.process_ie_result(info_dict.copy())
136 downloaded = ydl.downloaded_info_dicts[0]
137 self.assertEqual(downloaded['format_id'], '35')
139 def test_format_selection_audio(self):
141 {'format_id': 'audio-low', 'ext': 'webm', 'preference': 1, 'vcodec': 'none', 'url': TEST_URL},
142 {'format_id': 'audio-mid', 'ext': 'webm', 'preference': 2, 'vcodec': 'none', 'url': TEST_URL},
143 {'format_id': 'audio-high', 'ext': 'flv', 'preference': 3, 'vcodec': 'none', 'url': TEST_URL},
144 {'format_id': 'vid', 'ext': 'mp4', 'preference': 4, 'url': TEST_URL},
146 info_dict = _make_result(formats)
148 ydl = YDL({'format': 'bestaudio'})
149 ydl.process_ie_result(info_dict.copy())
150 downloaded = ydl.downloaded_info_dicts[0]
151 self.assertEqual(downloaded['format_id'], 'audio-high')
153 ydl = YDL({'format': 'worstaudio'})
154 ydl.process_ie_result(info_dict.copy())
155 downloaded = ydl.downloaded_info_dicts[0]
156 self.assertEqual(downloaded['format_id'], 'audio-low')
159 {'format_id': 'vid-low', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL},
160 {'format_id': 'vid-high', 'ext': 'mp4', 'preference': 2, 'url': TEST_URL},
162 info_dict = _make_result(formats)
164 ydl = YDL({'format': 'bestaudio/worstaudio/best'})
165 ydl.process_ie_result(info_dict.copy())
166 downloaded = ydl.downloaded_info_dicts[0]
167 self.assertEqual(downloaded['format_id'], 'vid-high')
169 def test_format_selection_audio_exts(self):
171 {'format_id': 'mp3-64', 'ext': 'mp3', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
172 {'format_id': 'ogg-64', 'ext': 'ogg', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
173 {'format_id': 'aac-64', 'ext': 'aac', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
174 {'format_id': 'mp3-32', 'ext': 'mp3', 'abr': 32, 'url': 'http://_', 'vcodec': 'none'},
175 {'format_id': 'aac-32', 'ext': 'aac', 'abr': 32, 'url': 'http://_', 'vcodec': 'none'},
178 info_dict = _make_result(formats)
179 ydl = YDL({'format': 'best'})
181 ie._sort_formats(info_dict['formats'])
182 ydl.process_ie_result(copy.deepcopy(info_dict))
183 downloaded = ydl.downloaded_info_dicts[0]
184 self.assertEqual(downloaded['format_id'], 'aac-64')
186 ydl = YDL({'format': 'mp3'})
188 ie._sort_formats(info_dict['formats'])
189 ydl.process_ie_result(copy.deepcopy(info_dict))
190 downloaded = ydl.downloaded_info_dicts[0]
191 self.assertEqual(downloaded['format_id'], 'mp3-64')
193 ydl = YDL({'prefer_free_formats': True})
195 ie._sort_formats(info_dict['formats'])
196 ydl.process_ie_result(copy.deepcopy(info_dict))
197 downloaded = ydl.downloaded_info_dicts[0]
198 self.assertEqual(downloaded['format_id'], 'ogg-64')
200 def test_format_selection_video(self):
202 {'format_id': 'dash-video-low', 'ext': 'mp4', 'preference': 1, 'acodec': 'none', 'url': TEST_URL},
203 {'format_id': 'dash-video-high', 'ext': 'mp4', 'preference': 2, 'acodec': 'none', 'url': TEST_URL},
204 {'format_id': 'vid', 'ext': 'mp4', 'preference': 3, 'url': TEST_URL},
206 info_dict = _make_result(formats)
208 ydl = YDL({'format': 'bestvideo'})
209 ydl.process_ie_result(info_dict.copy())
210 downloaded = ydl.downloaded_info_dicts[0]
211 self.assertEqual(downloaded['format_id'], 'dash-video-high')
213 ydl = YDL({'format': 'worstvideo'})
214 ydl.process_ie_result(info_dict.copy())
215 downloaded = ydl.downloaded_info_dicts[0]
216 self.assertEqual(downloaded['format_id'], 'dash-video-low')
218 def test_youtube_format_selection(self):
220 '38', '37', '46', '22', '45', '35', '44', '18', '34', '43', '6', '5', '36', '17', '13',
221 # Apple HTTP Live Streaming
222 '96', '95', '94', '93', '92', '132', '151',
224 '85', '84', '102', '83', '101', '82', '100',
226 '137', '248', '136', '247', '135', '246',
227 '245', '244', '134', '243', '133', '242', '160',
229 '141', '172', '140', '171', '139',
232 def format_info(f_id):
233 info = YoutubeIE._formats[f_id].copy()
234 info['format_id'] = f_id
235 info['url'] = 'url:' + f_id
237 formats_order = [format_info(f_id) for f_id in order]
239 info_dict = _make_result(list(formats_order), extractor='youtube')
240 ydl = YDL({'format': 'bestvideo+bestaudio'})
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'], '137+141')
246 self.assertEqual(downloaded['ext'], 'mp4')
248 info_dict = _make_result(list(formats_order), extractor='youtube')
249 ydl = YDL({'format': 'bestvideo[height>=999999]+bestaudio/best'})
251 yie._sort_formats(info_dict['formats'])
252 ydl.process_ie_result(info_dict)
253 downloaded = ydl.downloaded_info_dicts[0]
254 self.assertEqual(downloaded['format_id'], '38')
256 info_dict = _make_result(list(formats_order), extractor='youtube')
257 ydl = YDL({'format': 'bestvideo/best,bestaudio'})
259 yie._sort_formats(info_dict['formats'])
260 ydl.process_ie_result(info_dict)
261 downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
262 self.assertEqual(downloaded_ids, ['137', '141'])
264 info_dict = _make_result(list(formats_order), extractor='youtube')
265 ydl = YDL({'format': '(bestvideo[ext=mp4],bestvideo[ext=webm])+bestaudio'})
267 yie._sort_formats(info_dict['formats'])
268 ydl.process_ie_result(info_dict)
269 downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
270 self.assertEqual(downloaded_ids, ['137+141', '248+141'])
272 info_dict = _make_result(list(formats_order), extractor='youtube')
273 ydl = YDL({'format': '(bestvideo[ext=mp4],bestvideo[ext=webm])[height<=720]+bestaudio'})
275 yie._sort_formats(info_dict['formats'])
276 ydl.process_ie_result(info_dict)
277 downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
278 self.assertEqual(downloaded_ids, ['136+141', '247+141'])
280 info_dict = _make_result(list(formats_order), extractor='youtube')
281 ydl = YDL({'format': '(bestvideo[ext=none]/bestvideo[ext=webm])+bestaudio'})
283 yie._sort_formats(info_dict['formats'])
284 ydl.process_ie_result(info_dict)
285 downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
286 self.assertEqual(downloaded_ids, ['248+141'])
288 for f1, f2 in zip(formats_order, formats_order[1:]):
289 info_dict = _make_result([f1, f2], extractor='youtube')
290 ydl = YDL({'format': 'best/bestvideo'})
292 yie._sort_formats(info_dict['formats'])
293 ydl.process_ie_result(info_dict)
294 downloaded = ydl.downloaded_info_dicts[0]
295 self.assertEqual(downloaded['format_id'], f1['format_id'])
297 info_dict = _make_result([f2, f1], extractor='youtube')
298 ydl = YDL({'format': 'best/bestvideo'})
300 yie._sort_formats(info_dict['formats'])
301 ydl.process_ie_result(info_dict)
302 downloaded = ydl.downloaded_info_dicts[0]
303 self.assertEqual(downloaded['format_id'], f1['format_id'])
305 def test_invalid_format_specs(self):
306 def assert_syntax_error(format_spec):
307 ydl = YDL({'format': format_spec})
308 info_dict = _make_result([{'format_id': 'foo', 'url': TEST_URL}])
309 self.assertRaises(SyntaxError, ydl.process_ie_result, info_dict)
311 assert_syntax_error('bestvideo,,best')
312 assert_syntax_error('+bestaudio')
313 assert_syntax_error('bestvideo+')
314 assert_syntax_error('/')
316 def test_format_filtering(self):
318 {'format_id': 'A', 'filesize': 500, 'width': 1000},
319 {'format_id': 'B', 'filesize': 1000, 'width': 500},
320 {'format_id': 'C', 'filesize': 1000, 'width': 400},
321 {'format_id': 'D', 'filesize': 2000, 'width': 600},
322 {'format_id': 'E', 'filesize': 3000},
324 {'format_id': 'G', 'filesize': 1000000},
327 f['url'] = 'http://_/'
329 info_dict = _make_result(formats)
331 ydl = YDL({'format': 'best[filesize<3000]'})
332 ydl.process_ie_result(info_dict)
333 downloaded = ydl.downloaded_info_dicts[0]
334 self.assertEqual(downloaded['format_id'], 'D')
336 ydl = YDL({'format': 'best[filesize<=3000]'})
337 ydl.process_ie_result(info_dict)
338 downloaded = ydl.downloaded_info_dicts[0]
339 self.assertEqual(downloaded['format_id'], 'E')
341 ydl = YDL({'format': 'best[filesize <= ? 3000]'})
342 ydl.process_ie_result(info_dict)
343 downloaded = ydl.downloaded_info_dicts[0]
344 self.assertEqual(downloaded['format_id'], 'F')
346 ydl = YDL({'format': 'best [filesize = 1000] [width>450]'})
347 ydl.process_ie_result(info_dict)
348 downloaded = ydl.downloaded_info_dicts[0]
349 self.assertEqual(downloaded['format_id'], 'B')
351 ydl = YDL({'format': 'best [filesize = 1000] [width!=450]'})
352 ydl.process_ie_result(info_dict)
353 downloaded = ydl.downloaded_info_dicts[0]
354 self.assertEqual(downloaded['format_id'], 'C')
356 ydl = YDL({'format': '[filesize>?1]'})
357 ydl.process_ie_result(info_dict)
358 downloaded = ydl.downloaded_info_dicts[0]
359 self.assertEqual(downloaded['format_id'], 'G')
361 ydl = YDL({'format': '[filesize<1M]'})
362 ydl.process_ie_result(info_dict)
363 downloaded = ydl.downloaded_info_dicts[0]
364 self.assertEqual(downloaded['format_id'], 'E')
366 ydl = YDL({'format': '[filesize<1MiB]'})
367 ydl.process_ie_result(info_dict)
368 downloaded = ydl.downloaded_info_dicts[0]
369 self.assertEqual(downloaded['format_id'], 'G')
371 ydl = YDL({'format': 'all[width>=400][width<=600]'})
372 ydl.process_ie_result(info_dict)
373 downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
374 self.assertEqual(downloaded_ids, ['B', 'C', 'D'])
376 ydl = YDL({'format': 'best[height<40]'})
378 ydl.process_ie_result(info_dict)
379 except ExtractorError:
381 self.assertEqual(ydl.downloaded_info_dicts, [])
384 class TestYoutubeDL(unittest.TestCase):
385 def test_subtitles(self):
386 def s_formats(lang, autocaption=False):
389 'url': 'http://localhost/video.%s.%s' % (lang, ext),
390 '_auto': autocaption,
391 } for ext in ['vtt', 'srt', 'ass']]
392 subtitles = dict((l, s_formats(l)) for l in ['en', 'fr', 'es'])
393 auto_captions = dict((l, s_formats(l, True)) for l in ['it', 'pt', 'es'])
397 'url': 'http://localhost/video.mp4',
398 'subtitles': subtitles,
399 'automatic_captions': auto_captions,
403 def get_info(params={}):
404 params.setdefault('simulate', True)
406 ydl.report_warning = lambda *args, **kargs: None
407 return ydl.process_video_result(info_dict, download=False)
410 self.assertFalse(result.get('requested_subtitles'))
411 self.assertEqual(result['subtitles'], subtitles)
412 self.assertEqual(result['automatic_captions'], auto_captions)
414 result = get_info({'writesubtitles': True})
415 subs = result['requested_subtitles']
416 self.assertTrue(subs)
417 self.assertEqual(set(subs.keys()), set(['en']))
418 self.assertTrue(subs['en'].get('data') is None)
419 self.assertEqual(subs['en']['ext'], 'ass')
421 result = get_info({'writesubtitles': True, 'subtitlesformat': 'foo/srt'})
422 subs = result['requested_subtitles']
423 self.assertEqual(subs['en']['ext'], 'srt')
425 result = get_info({'writesubtitles': True, 'subtitleslangs': ['es', 'fr', 'it']})
426 subs = result['requested_subtitles']
427 self.assertTrue(subs)
428 self.assertEqual(set(subs.keys()), set(['es', 'fr']))
430 result = get_info({'writesubtitles': True, 'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
431 subs = result['requested_subtitles']
432 self.assertTrue(subs)
433 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
434 self.assertFalse(subs['es']['_auto'])
435 self.assertTrue(subs['pt']['_auto'])
437 result = get_info({'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
438 subs = result['requested_subtitles']
439 self.assertTrue(subs)
440 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
441 self.assertTrue(subs['es']['_auto'])
442 self.assertTrue(subs['pt']['_auto'])
444 def test_add_extra_info(self):
450 'playlist': 'funny videos',
452 YDL.add_extra_info(test_dict, extra_info)
453 self.assertEqual(test_dict['extractor'], 'Foo')
454 self.assertEqual(test_dict['playlist'], 'funny videos')
456 def test_prepare_filename(self):
464 ydl = YoutubeDL({'outtmpl': templ})
465 return ydl.prepare_filename(info)
466 self.assertEqual(fname('%(id)s.%(ext)s'), '1234.mp4')
467 self.assertEqual(fname('%(id)s-%(width)s.%(ext)s'), '1234-NA.mp4')
468 # Replace missing fields with 'NA'
469 self.assertEqual(fname('%(uploader_date)s-%(id)s.%(ext)s'), 'NA-1234.mp4')
471 def test_format_note(self):
473 self.assertEqual(ydl._format_note({}), '')
474 assertRegexpMatches(self, ydl._format_note({
478 def test_postprocessors(self):
479 filename = 'post-processor-testfile.mp4'
480 audiofile = filename + '.mp3'
482 class SimplePP(PostProcessor):
484 with open(audiofile, 'wt') as f:
486 return [info['filepath']], info
488 def run_pp(params, PP):
489 with open(filename, 'wt') as f:
491 ydl = YoutubeDL(params)
492 ydl.add_post_processor(PP())
493 ydl.post_process(filename, {'filepath': filename})
495 run_pp({'keepvideo': True}, SimplePP)
496 self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
497 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
501 run_pp({'keepvideo': False}, SimplePP)
502 self.assertFalse(os.path.exists(filename), '%s exists' % filename)
503 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
506 class ModifierPP(PostProcessor):
508 with open(info['filepath'], 'wt') as f:
512 run_pp({'keepvideo': False}, ModifierPP)
513 self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
516 def test_match_filter(self):
517 class FilterYDL(YDL):
518 def __init__(self, *args, **kwargs):
519 super(FilterYDL, self).__init__(*args, **kwargs)
520 self.params['simulate'] = True
522 def process_info(self, info_dict):
523 super(YDL, self).process_info(info_dict)
525 def _match_entry(self, info_dict, incomplete):
526 res = super(FilterYDL, self)._match_entry(info_dict, incomplete)
528 self.downloaded_info_dicts.append(info_dict)
537 'filesize': 10 * 1024,
545 'description': 'foo',
546 'filesize': 5 * 1024,
548 videos = [first, second]
550 def get_videos(filter_=None):
551 ydl = FilterYDL({'match_filter': filter_})
553 ydl.process_ie_result(v, download=True)
554 return [v['id'] for v in ydl.downloaded_info_dicts]
557 self.assertEqual(res, ['1', '2'])
563 return 'Video id is not 1'
565 self.assertEqual(res, ['1'])
567 f = match_filter_func('duration < 30')
569 self.assertEqual(res, ['2'])
571 f = match_filter_func('description = foo')
573 self.assertEqual(res, ['2'])
575 f = match_filter_func('description =? foo')
577 self.assertEqual(res, ['1', '2'])
579 f = match_filter_func('filesize > 5KiB')
581 self.assertEqual(res, ['1'])
583 def test_playlist_items_selection(self):
586 'title': compat_str(i),
588 } for i in range(1, 5)]
593 'extractor': 'test:playlist',
594 'extractor_key': 'test:playlist',
595 'webpage_url': 'http://example.com',
600 # make a copy because the dictionary can be modified
601 ydl.process_ie_result(playlist.copy())
602 return [int(v['id']) for v in ydl.downloaded_info_dicts]
605 self.assertEqual(result, [1, 2, 3, 4])
607 result = get_ids({'playlistend': 10})
608 self.assertEqual(result, [1, 2, 3, 4])
610 result = get_ids({'playlistend': 2})
611 self.assertEqual(result, [1, 2])
613 result = get_ids({'playliststart': 10})
614 self.assertEqual(result, [])
616 result = get_ids({'playliststart': 2})
617 self.assertEqual(result, [2, 3, 4])
619 result = get_ids({'playlist_items': '2-4'})
620 self.assertEqual(result, [2, 3, 4])
622 result = get_ids({'playlist_items': '2,4'})
623 self.assertEqual(result, [2, 4])
625 result = get_ids({'playlist_items': '10'})
626 self.assertEqual(result, [])
629 if __name__ == '__main__':