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/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_format_filtering(self):
307 {'format_id': 'A', 'filesize': 500, 'width': 1000},
308 {'format_id': 'B', 'filesize': 1000, 'width': 500},
309 {'format_id': 'C', 'filesize': 1000, 'width': 400},
310 {'format_id': 'D', 'filesize': 2000, 'width': 600},
311 {'format_id': 'E', 'filesize': 3000},
313 {'format_id': 'G', 'filesize': 1000000},
316 f['url'] = 'http://_/'
318 info_dict = _make_result(formats)
320 ydl = YDL({'format': 'best[filesize<3000]'})
321 ydl.process_ie_result(info_dict)
322 downloaded = ydl.downloaded_info_dicts[0]
323 self.assertEqual(downloaded['format_id'], 'D')
325 ydl = YDL({'format': 'best[filesize<=3000]'})
326 ydl.process_ie_result(info_dict)
327 downloaded = ydl.downloaded_info_dicts[0]
328 self.assertEqual(downloaded['format_id'], 'E')
330 ydl = YDL({'format': 'best[filesize <= ? 3000]'})
331 ydl.process_ie_result(info_dict)
332 downloaded = ydl.downloaded_info_dicts[0]
333 self.assertEqual(downloaded['format_id'], 'F')
335 ydl = YDL({'format': 'best [filesize = 1000] [width>450]'})
336 ydl.process_ie_result(info_dict)
337 downloaded = ydl.downloaded_info_dicts[0]
338 self.assertEqual(downloaded['format_id'], 'B')
340 ydl = YDL({'format': 'best [filesize = 1000] [width!=450]'})
341 ydl.process_ie_result(info_dict)
342 downloaded = ydl.downloaded_info_dicts[0]
343 self.assertEqual(downloaded['format_id'], 'C')
345 ydl = YDL({'format': '[filesize>?1]'})
346 ydl.process_ie_result(info_dict)
347 downloaded = ydl.downloaded_info_dicts[0]
348 self.assertEqual(downloaded['format_id'], 'G')
350 ydl = YDL({'format': '[filesize<1M]'})
351 ydl.process_ie_result(info_dict)
352 downloaded = ydl.downloaded_info_dicts[0]
353 self.assertEqual(downloaded['format_id'], 'E')
355 ydl = YDL({'format': '[filesize<1MiB]'})
356 ydl.process_ie_result(info_dict)
357 downloaded = ydl.downloaded_info_dicts[0]
358 self.assertEqual(downloaded['format_id'], 'G')
360 ydl = YDL({'format': 'all[width>=400][width<=600]'})
361 ydl.process_ie_result(info_dict)
362 downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
363 self.assertEqual(downloaded_ids, ['B', 'C', 'D'])
366 class TestYoutubeDL(unittest.TestCase):
367 def test_subtitles(self):
368 def s_formats(lang, autocaption=False):
371 'url': 'http://localhost/video.%s.%s' % (lang, ext),
372 '_auto': autocaption,
373 } for ext in ['vtt', 'srt', 'ass']]
374 subtitles = dict((l, s_formats(l)) for l in ['en', 'fr', 'es'])
375 auto_captions = dict((l, s_formats(l, True)) for l in ['it', 'pt', 'es'])
379 'url': 'http://localhost/video.mp4',
380 'subtitles': subtitles,
381 'automatic_captions': auto_captions,
385 def get_info(params={}):
386 params.setdefault('simulate', True)
388 ydl.report_warning = lambda *args, **kargs: None
389 return ydl.process_video_result(info_dict, download=False)
392 self.assertFalse(result.get('requested_subtitles'))
393 self.assertEqual(result['subtitles'], subtitles)
394 self.assertEqual(result['automatic_captions'], auto_captions)
396 result = get_info({'writesubtitles': True})
397 subs = result['requested_subtitles']
398 self.assertTrue(subs)
399 self.assertEqual(set(subs.keys()), set(['en']))
400 self.assertTrue(subs['en'].get('data') is None)
401 self.assertEqual(subs['en']['ext'], 'ass')
403 result = get_info({'writesubtitles': True, 'subtitlesformat': 'foo/srt'})
404 subs = result['requested_subtitles']
405 self.assertEqual(subs['en']['ext'], 'srt')
407 result = get_info({'writesubtitles': True, 'subtitleslangs': ['es', 'fr', 'it']})
408 subs = result['requested_subtitles']
409 self.assertTrue(subs)
410 self.assertEqual(set(subs.keys()), set(['es', 'fr']))
412 result = get_info({'writesubtitles': True, 'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
413 subs = result['requested_subtitles']
414 self.assertTrue(subs)
415 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
416 self.assertFalse(subs['es']['_auto'])
417 self.assertTrue(subs['pt']['_auto'])
419 result = get_info({'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
420 subs = result['requested_subtitles']
421 self.assertTrue(subs)
422 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
423 self.assertTrue(subs['es']['_auto'])
424 self.assertTrue(subs['pt']['_auto'])
426 def test_add_extra_info(self):
432 'playlist': 'funny videos',
434 YDL.add_extra_info(test_dict, extra_info)
435 self.assertEqual(test_dict['extractor'], 'Foo')
436 self.assertEqual(test_dict['playlist'], 'funny videos')
438 def test_prepare_filename(self):
446 ydl = YoutubeDL({'outtmpl': templ})
447 return ydl.prepare_filename(info)
448 self.assertEqual(fname('%(id)s.%(ext)s'), '1234.mp4')
449 self.assertEqual(fname('%(id)s-%(width)s.%(ext)s'), '1234-NA.mp4')
450 # Replace missing fields with 'NA'
451 self.assertEqual(fname('%(uploader_date)s-%(id)s.%(ext)s'), 'NA-1234.mp4')
453 def test_format_note(self):
455 self.assertEqual(ydl._format_note({}), '')
456 assertRegexpMatches(self, ydl._format_note({
460 def test_postprocessors(self):
461 filename = 'post-processor-testfile.mp4'
462 audiofile = filename + '.mp3'
464 class SimplePP(PostProcessor):
466 with open(audiofile, 'wt') as f:
468 return [info['filepath']], info
470 def run_pp(params, PP):
471 with open(filename, 'wt') as f:
473 ydl = YoutubeDL(params)
474 ydl.add_post_processor(PP())
475 ydl.post_process(filename, {'filepath': filename})
477 run_pp({'keepvideo': True}, SimplePP)
478 self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
479 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
483 run_pp({'keepvideo': False}, SimplePP)
484 self.assertFalse(os.path.exists(filename), '%s exists' % filename)
485 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
488 class ModifierPP(PostProcessor):
490 with open(info['filepath'], 'wt') as f:
494 run_pp({'keepvideo': False}, ModifierPP)
495 self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
498 def test_match_filter(self):
499 class FilterYDL(YDL):
500 def __init__(self, *args, **kwargs):
501 super(FilterYDL, self).__init__(*args, **kwargs)
502 self.params['simulate'] = True
504 def process_info(self, info_dict):
505 super(YDL, self).process_info(info_dict)
507 def _match_entry(self, info_dict, incomplete):
508 res = super(FilterYDL, self)._match_entry(info_dict, incomplete)
510 self.downloaded_info_dicts.append(info_dict)
519 'filesize': 10 * 1024,
527 'description': 'foo',
528 'filesize': 5 * 1024,
530 videos = [first, second]
532 def get_videos(filter_=None):
533 ydl = FilterYDL({'match_filter': filter_})
535 ydl.process_ie_result(v, download=True)
536 return [v['id'] for v in ydl.downloaded_info_dicts]
539 self.assertEqual(res, ['1', '2'])
545 return 'Video id is not 1'
547 self.assertEqual(res, ['1'])
549 f = match_filter_func('duration < 30')
551 self.assertEqual(res, ['2'])
553 f = match_filter_func('description = foo')
555 self.assertEqual(res, ['2'])
557 f = match_filter_func('description =? foo')
559 self.assertEqual(res, ['1', '2'])
561 f = match_filter_func('filesize > 5KiB')
563 self.assertEqual(res, ['1'])
565 def test_playlist_items_selection(self):
568 'title': compat_str(i),
570 } for i in range(1, 5)]
575 'extractor': 'test:playlist',
576 'extractor_key': 'test:playlist',
577 'webpage_url': 'http://example.com',
582 # make a copy because the dictionary can be modified
583 ydl.process_ie_result(playlist.copy())
584 return [int(v['id']) for v in ydl.downloaded_info_dicts]
587 self.assertEqual(result, [1, 2, 3, 4])
589 result = get_ids({'playlistend': 10})
590 self.assertEqual(result, [1, 2, 3, 4])
592 result = get_ids({'playlistend': 2})
593 self.assertEqual(result, [1, 2])
595 result = get_ids({'playliststart': 10})
596 self.assertEqual(result, [])
598 result = get_ids({'playliststart': 2})
599 self.assertEqual(result, [2, 3, 4])
601 result = get_ids({'playlist_items': '2-4'})
602 self.assertEqual(result, [2, 3, 4])
604 result = get_ids({'playlist_items': '2,4'})
605 self.assertEqual(result, [2, 4])
607 result = get_ids({'playlist_items': '10'})
608 self.assertEqual(result, [])
611 if __name__ == '__main__':