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 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[ext=mp4],bestvideo[ext=webm])+bestaudio'})
251 yie._sort_formats(info_dict['formats'])
252 ydl.process_ie_result(info_dict)
253 downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
254 self.assertEqual(downloaded_ids, ['137+141', '248+141'])
256 info_dict = _make_result(list(formats_order), extractor='youtube')
257 ydl = YDL({'format': '(bestvideo[ext=mp4],bestvideo[ext=webm])[height<=720]+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, ['136+141', '247+141'])
264 info_dict = _make_result(list(formats_order), extractor='youtube')
265 ydl = YDL({'format': '(bestvideo[ext=none]/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, ['248+141'])
272 for f1, f2 in zip(formats_order, formats_order[1:]):
273 info_dict = _make_result([f1, f2], extractor='youtube')
274 ydl = YDL({'format': 'best/bestvideo'})
276 yie._sort_formats(info_dict['formats'])
277 ydl.process_ie_result(info_dict)
278 downloaded = ydl.downloaded_info_dicts[0]
279 self.assertEqual(downloaded['format_id'], f1['format_id'])
281 info_dict = _make_result([f2, f1], extractor='youtube')
282 ydl = YDL({'format': 'best/bestvideo'})
284 yie._sort_formats(info_dict['formats'])
285 ydl.process_ie_result(info_dict)
286 downloaded = ydl.downloaded_info_dicts[0]
287 self.assertEqual(downloaded['format_id'], f1['format_id'])
289 def test_format_filtering(self):
291 {'format_id': 'A', 'filesize': 500, 'width': 1000},
292 {'format_id': 'B', 'filesize': 1000, 'width': 500},
293 {'format_id': 'C', 'filesize': 1000, 'width': 400},
294 {'format_id': 'D', 'filesize': 2000, 'width': 600},
295 {'format_id': 'E', 'filesize': 3000},
297 {'format_id': 'G', 'filesize': 1000000},
300 f['url'] = 'http://_/'
302 info_dict = _make_result(formats)
304 ydl = YDL({'format': 'best[filesize<3000]'})
305 ydl.process_ie_result(info_dict)
306 downloaded = ydl.downloaded_info_dicts[0]
307 self.assertEqual(downloaded['format_id'], 'D')
309 ydl = YDL({'format': 'best[filesize<=3000]'})
310 ydl.process_ie_result(info_dict)
311 downloaded = ydl.downloaded_info_dicts[0]
312 self.assertEqual(downloaded['format_id'], 'E')
314 ydl = YDL({'format': 'best[filesize <= ? 3000]'})
315 ydl.process_ie_result(info_dict)
316 downloaded = ydl.downloaded_info_dicts[0]
317 self.assertEqual(downloaded['format_id'], 'F')
319 ydl = YDL({'format': 'best [filesize = 1000] [width>450]'})
320 ydl.process_ie_result(info_dict)
321 downloaded = ydl.downloaded_info_dicts[0]
322 self.assertEqual(downloaded['format_id'], 'B')
324 ydl = YDL({'format': 'best [filesize = 1000] [width!=450]'})
325 ydl.process_ie_result(info_dict)
326 downloaded = ydl.downloaded_info_dicts[0]
327 self.assertEqual(downloaded['format_id'], 'C')
329 ydl = YDL({'format': '[filesize>?1]'})
330 ydl.process_ie_result(info_dict)
331 downloaded = ydl.downloaded_info_dicts[0]
332 self.assertEqual(downloaded['format_id'], 'G')
334 ydl = YDL({'format': '[filesize<1M]'})
335 ydl.process_ie_result(info_dict)
336 downloaded = ydl.downloaded_info_dicts[0]
337 self.assertEqual(downloaded['format_id'], 'E')
339 ydl = YDL({'format': '[filesize<1MiB]'})
340 ydl.process_ie_result(info_dict)
341 downloaded = ydl.downloaded_info_dicts[0]
342 self.assertEqual(downloaded['format_id'], 'G')
344 ydl = YDL({'format': 'all[width>=400][width<=600]'})
345 ydl.process_ie_result(info_dict)
346 downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
347 self.assertEqual(downloaded_ids, ['B', 'C', 'D'])
350 class TestYoutubeDL(unittest.TestCase):
351 def test_subtitles(self):
352 def s_formats(lang, autocaption=False):
355 'url': 'http://localhost/video.%s.%s' % (lang, ext),
356 '_auto': autocaption,
357 } for ext in ['vtt', 'srt', 'ass']]
358 subtitles = dict((l, s_formats(l)) for l in ['en', 'fr', 'es'])
359 auto_captions = dict((l, s_formats(l, True)) for l in ['it', 'pt', 'es'])
363 'url': 'http://localhost/video.mp4',
364 'subtitles': subtitles,
365 'automatic_captions': auto_captions,
369 def get_info(params={}):
370 params.setdefault('simulate', True)
372 ydl.report_warning = lambda *args, **kargs: None
373 return ydl.process_video_result(info_dict, download=False)
376 self.assertFalse(result.get('requested_subtitles'))
377 self.assertEqual(result['subtitles'], subtitles)
378 self.assertEqual(result['automatic_captions'], auto_captions)
380 result = get_info({'writesubtitles': True})
381 subs = result['requested_subtitles']
382 self.assertTrue(subs)
383 self.assertEqual(set(subs.keys()), set(['en']))
384 self.assertTrue(subs['en'].get('data') is None)
385 self.assertEqual(subs['en']['ext'], 'ass')
387 result = get_info({'writesubtitles': True, 'subtitlesformat': 'foo/srt'})
388 subs = result['requested_subtitles']
389 self.assertEqual(subs['en']['ext'], 'srt')
391 result = get_info({'writesubtitles': True, 'subtitleslangs': ['es', 'fr', 'it']})
392 subs = result['requested_subtitles']
393 self.assertTrue(subs)
394 self.assertEqual(set(subs.keys()), set(['es', 'fr']))
396 result = get_info({'writesubtitles': True, 'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
397 subs = result['requested_subtitles']
398 self.assertTrue(subs)
399 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
400 self.assertFalse(subs['es']['_auto'])
401 self.assertTrue(subs['pt']['_auto'])
403 result = get_info({'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
404 subs = result['requested_subtitles']
405 self.assertTrue(subs)
406 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
407 self.assertTrue(subs['es']['_auto'])
408 self.assertTrue(subs['pt']['_auto'])
410 def test_add_extra_info(self):
416 'playlist': 'funny videos',
418 YDL.add_extra_info(test_dict, extra_info)
419 self.assertEqual(test_dict['extractor'], 'Foo')
420 self.assertEqual(test_dict['playlist'], 'funny videos')
422 def test_prepare_filename(self):
430 ydl = YoutubeDL({'outtmpl': templ})
431 return ydl.prepare_filename(info)
432 self.assertEqual(fname('%(id)s.%(ext)s'), '1234.mp4')
433 self.assertEqual(fname('%(id)s-%(width)s.%(ext)s'), '1234-NA.mp4')
434 # Replace missing fields with 'NA'
435 self.assertEqual(fname('%(uploader_date)s-%(id)s.%(ext)s'), 'NA-1234.mp4')
437 def test_format_note(self):
439 self.assertEqual(ydl._format_note({}), '')
440 assertRegexpMatches(self, ydl._format_note({
444 def test_postprocessors(self):
445 filename = 'post-processor-testfile.mp4'
446 audiofile = filename + '.mp3'
448 class SimplePP(PostProcessor):
450 with open(audiofile, 'wt') as f:
452 return [info['filepath']], info
454 def run_pp(params, PP):
455 with open(filename, 'wt') as f:
457 ydl = YoutubeDL(params)
458 ydl.add_post_processor(PP())
459 ydl.post_process(filename, {'filepath': filename})
461 run_pp({'keepvideo': True}, SimplePP)
462 self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
463 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
467 run_pp({'keepvideo': False}, SimplePP)
468 self.assertFalse(os.path.exists(filename), '%s exists' % filename)
469 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
472 class ModifierPP(PostProcessor):
474 with open(info['filepath'], 'wt') as f:
478 run_pp({'keepvideo': False}, ModifierPP)
479 self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
482 def test_match_filter(self):
483 class FilterYDL(YDL):
484 def __init__(self, *args, **kwargs):
485 super(FilterYDL, self).__init__(*args, **kwargs)
486 self.params['simulate'] = True
488 def process_info(self, info_dict):
489 super(YDL, self).process_info(info_dict)
491 def _match_entry(self, info_dict, incomplete):
492 res = super(FilterYDL, self)._match_entry(info_dict, incomplete)
494 self.downloaded_info_dicts.append(info_dict)
503 'filesize': 10 * 1024,
511 'description': 'foo',
512 'filesize': 5 * 1024,
514 videos = [first, second]
516 def get_videos(filter_=None):
517 ydl = FilterYDL({'match_filter': filter_})
519 ydl.process_ie_result(v, download=True)
520 return [v['id'] for v in ydl.downloaded_info_dicts]
523 self.assertEqual(res, ['1', '2'])
529 return 'Video id is not 1'
531 self.assertEqual(res, ['1'])
533 f = match_filter_func('duration < 30')
535 self.assertEqual(res, ['2'])
537 f = match_filter_func('description = foo')
539 self.assertEqual(res, ['2'])
541 f = match_filter_func('description =? foo')
543 self.assertEqual(res, ['1', '2'])
545 f = match_filter_func('filesize > 5KiB')
547 self.assertEqual(res, ['1'])
549 def test_playlist_items_selection(self):
552 'title': compat_str(i),
554 } for i in range(1, 5)]
559 'extractor': 'test:playlist',
560 'extractor_key': 'test:playlist',
561 'webpage_url': 'http://example.com',
566 # make a copy because the dictionary can be modified
567 ydl.process_ie_result(playlist.copy())
568 return [int(v['id']) for v in ydl.downloaded_info_dicts]
571 self.assertEqual(result, [1, 2, 3, 4])
573 result = get_ids({'playlistend': 10})
574 self.assertEqual(result, [1, 2, 3, 4])
576 result = get_ids({'playlistend': 2})
577 self.assertEqual(result, [1, 2])
579 result = get_ids({'playliststart': 10})
580 self.assertEqual(result, [])
582 result = get_ids({'playliststart': 2})
583 self.assertEqual(result, [2, 3, 4])
585 result = get_ids({'playlist_items': '2-4'})
586 self.assertEqual(result, [2, 3, 4])
588 result = get_ids({'playlist_items': '2,4'})
589 self.assertEqual(result, [2, 4])
591 result = get_ids({'playlist_items': '10'})
592 self.assertEqual(result, [])
595 if __name__ == '__main__':