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[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[ext=mp4],bestvideo[ext=webm])+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', '248+141'])
264 info_dict = _make_result(list(formats_order), extractor='youtube')
265 ydl = YDL({'format': '(bestvideo[ext=mp4],bestvideo[ext=webm])[height<=720]+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, ['136+141', '247+141'])
272 info_dict = _make_result(list(formats_order), extractor='youtube')
273 ydl = YDL({'format': '(bestvideo[ext=none]/bestvideo[ext=webm])+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, ['248+141'])
280 for f1, f2 in zip(formats_order, formats_order[1:]):
281 info_dict = _make_result([f1, f2], 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 info_dict = _make_result([f2, f1], 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 def test_format_filtering(self):
299 {'format_id': 'A', 'filesize': 500, 'width': 1000},
300 {'format_id': 'B', 'filesize': 1000, 'width': 500},
301 {'format_id': 'C', 'filesize': 1000, 'width': 400},
302 {'format_id': 'D', 'filesize': 2000, 'width': 600},
303 {'format_id': 'E', 'filesize': 3000},
305 {'format_id': 'G', 'filesize': 1000000},
308 f['url'] = 'http://_/'
310 info_dict = _make_result(formats)
312 ydl = YDL({'format': 'best[filesize<3000]'})
313 ydl.process_ie_result(info_dict)
314 downloaded = ydl.downloaded_info_dicts[0]
315 self.assertEqual(downloaded['format_id'], 'D')
317 ydl = YDL({'format': 'best[filesize<=3000]'})
318 ydl.process_ie_result(info_dict)
319 downloaded = ydl.downloaded_info_dicts[0]
320 self.assertEqual(downloaded['format_id'], 'E')
322 ydl = YDL({'format': 'best[filesize <= ? 3000]'})
323 ydl.process_ie_result(info_dict)
324 downloaded = ydl.downloaded_info_dicts[0]
325 self.assertEqual(downloaded['format_id'], 'F')
327 ydl = YDL({'format': 'best [filesize = 1000] [width>450]'})
328 ydl.process_ie_result(info_dict)
329 downloaded = ydl.downloaded_info_dicts[0]
330 self.assertEqual(downloaded['format_id'], 'B')
332 ydl = YDL({'format': 'best [filesize = 1000] [width!=450]'})
333 ydl.process_ie_result(info_dict)
334 downloaded = ydl.downloaded_info_dicts[0]
335 self.assertEqual(downloaded['format_id'], 'C')
337 ydl = YDL({'format': '[filesize>?1]'})
338 ydl.process_ie_result(info_dict)
339 downloaded = ydl.downloaded_info_dicts[0]
340 self.assertEqual(downloaded['format_id'], 'G')
342 ydl = YDL({'format': '[filesize<1M]'})
343 ydl.process_ie_result(info_dict)
344 downloaded = ydl.downloaded_info_dicts[0]
345 self.assertEqual(downloaded['format_id'], 'E')
347 ydl = YDL({'format': '[filesize<1MiB]'})
348 ydl.process_ie_result(info_dict)
349 downloaded = ydl.downloaded_info_dicts[0]
350 self.assertEqual(downloaded['format_id'], 'G')
352 ydl = YDL({'format': 'all[width>=400][width<=600]'})
353 ydl.process_ie_result(info_dict)
354 downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
355 self.assertEqual(downloaded_ids, ['B', 'C', 'D'])
358 class TestYoutubeDL(unittest.TestCase):
359 def test_subtitles(self):
360 def s_formats(lang, autocaption=False):
363 'url': 'http://localhost/video.%s.%s' % (lang, ext),
364 '_auto': autocaption,
365 } for ext in ['vtt', 'srt', 'ass']]
366 subtitles = dict((l, s_formats(l)) for l in ['en', 'fr', 'es'])
367 auto_captions = dict((l, s_formats(l, True)) for l in ['it', 'pt', 'es'])
371 'url': 'http://localhost/video.mp4',
372 'subtitles': subtitles,
373 'automatic_captions': auto_captions,
377 def get_info(params={}):
378 params.setdefault('simulate', True)
380 ydl.report_warning = lambda *args, **kargs: None
381 return ydl.process_video_result(info_dict, download=False)
384 self.assertFalse(result.get('requested_subtitles'))
385 self.assertEqual(result['subtitles'], subtitles)
386 self.assertEqual(result['automatic_captions'], auto_captions)
388 result = get_info({'writesubtitles': True})
389 subs = result['requested_subtitles']
390 self.assertTrue(subs)
391 self.assertEqual(set(subs.keys()), set(['en']))
392 self.assertTrue(subs['en'].get('data') is None)
393 self.assertEqual(subs['en']['ext'], 'ass')
395 result = get_info({'writesubtitles': True, 'subtitlesformat': 'foo/srt'})
396 subs = result['requested_subtitles']
397 self.assertEqual(subs['en']['ext'], 'srt')
399 result = get_info({'writesubtitles': True, 'subtitleslangs': ['es', 'fr', 'it']})
400 subs = result['requested_subtitles']
401 self.assertTrue(subs)
402 self.assertEqual(set(subs.keys()), set(['es', 'fr']))
404 result = get_info({'writesubtitles': True, 'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
405 subs = result['requested_subtitles']
406 self.assertTrue(subs)
407 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
408 self.assertFalse(subs['es']['_auto'])
409 self.assertTrue(subs['pt']['_auto'])
411 result = get_info({'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
412 subs = result['requested_subtitles']
413 self.assertTrue(subs)
414 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
415 self.assertTrue(subs['es']['_auto'])
416 self.assertTrue(subs['pt']['_auto'])
418 def test_add_extra_info(self):
424 'playlist': 'funny videos',
426 YDL.add_extra_info(test_dict, extra_info)
427 self.assertEqual(test_dict['extractor'], 'Foo')
428 self.assertEqual(test_dict['playlist'], 'funny videos')
430 def test_prepare_filename(self):
438 ydl = YoutubeDL({'outtmpl': templ})
439 return ydl.prepare_filename(info)
440 self.assertEqual(fname('%(id)s.%(ext)s'), '1234.mp4')
441 self.assertEqual(fname('%(id)s-%(width)s.%(ext)s'), '1234-NA.mp4')
442 # Replace missing fields with 'NA'
443 self.assertEqual(fname('%(uploader_date)s-%(id)s.%(ext)s'), 'NA-1234.mp4')
445 def test_format_note(self):
447 self.assertEqual(ydl._format_note({}), '')
448 assertRegexpMatches(self, ydl._format_note({
452 def test_postprocessors(self):
453 filename = 'post-processor-testfile.mp4'
454 audiofile = filename + '.mp3'
456 class SimplePP(PostProcessor):
458 with open(audiofile, 'wt') as f:
460 return [info['filepath']], info
462 def run_pp(params, PP):
463 with open(filename, 'wt') as f:
465 ydl = YoutubeDL(params)
466 ydl.add_post_processor(PP())
467 ydl.post_process(filename, {'filepath': filename})
469 run_pp({'keepvideo': True}, SimplePP)
470 self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
471 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
475 run_pp({'keepvideo': False}, SimplePP)
476 self.assertFalse(os.path.exists(filename), '%s exists' % filename)
477 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
480 class ModifierPP(PostProcessor):
482 with open(info['filepath'], 'wt') as f:
486 run_pp({'keepvideo': False}, ModifierPP)
487 self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
490 def test_match_filter(self):
491 class FilterYDL(YDL):
492 def __init__(self, *args, **kwargs):
493 super(FilterYDL, self).__init__(*args, **kwargs)
494 self.params['simulate'] = True
496 def process_info(self, info_dict):
497 super(YDL, self).process_info(info_dict)
499 def _match_entry(self, info_dict, incomplete):
500 res = super(FilterYDL, self)._match_entry(info_dict, incomplete)
502 self.downloaded_info_dicts.append(info_dict)
511 'filesize': 10 * 1024,
519 'description': 'foo',
520 'filesize': 5 * 1024,
522 videos = [first, second]
524 def get_videos(filter_=None):
525 ydl = FilterYDL({'match_filter': filter_})
527 ydl.process_ie_result(v, download=True)
528 return [v['id'] for v in ydl.downloaded_info_dicts]
531 self.assertEqual(res, ['1', '2'])
537 return 'Video id is not 1'
539 self.assertEqual(res, ['1'])
541 f = match_filter_func('duration < 30')
543 self.assertEqual(res, ['2'])
545 f = match_filter_func('description = foo')
547 self.assertEqual(res, ['2'])
549 f = match_filter_func('description =? foo')
551 self.assertEqual(res, ['1', '2'])
553 f = match_filter_func('filesize > 5KiB')
555 self.assertEqual(res, ['1'])
557 def test_playlist_items_selection(self):
560 'title': compat_str(i),
562 } for i in range(1, 5)]
567 'extractor': 'test:playlist',
568 'extractor_key': 'test:playlist',
569 'webpage_url': 'http://example.com',
574 # make a copy because the dictionary can be modified
575 ydl.process_ie_result(playlist.copy())
576 return [int(v['id']) for v in ydl.downloaded_info_dicts]
579 self.assertEqual(result, [1, 2, 3, 4])
581 result = get_ids({'playlistend': 10})
582 self.assertEqual(result, [1, 2, 3, 4])
584 result = get_ids({'playlistend': 2})
585 self.assertEqual(result, [1, 2])
587 result = get_ids({'playliststart': 10})
588 self.assertEqual(result, [])
590 result = get_ids({'playliststart': 2})
591 self.assertEqual(result, [2, 3, 4])
593 result = get_ids({'playlist_items': '2-4'})
594 self.assertEqual(result, [2, 3, 4])
596 result = get_ids({'playlist_items': '2,4'})
597 self.assertEqual(result, [2, 4])
599 result = get_ids({'playlist_items': '10'})
600 self.assertEqual(result, [])
603 if __name__ == '__main__':