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 for f1, f2 in zip(formats_order, formats_order[1:]):
249 info_dict = _make_result([f1, f2], extractor='youtube')
250 ydl = YDL({'format': 'best/bestvideo'})
252 yie._sort_formats(info_dict['formats'])
253 ydl.process_ie_result(info_dict)
254 downloaded = ydl.downloaded_info_dicts[0]
255 self.assertEqual(downloaded['format_id'], f1['format_id'])
257 info_dict = _make_result([f2, f1], extractor='youtube')
258 ydl = YDL({'format': 'best/bestvideo'})
260 yie._sort_formats(info_dict['formats'])
261 ydl.process_ie_result(info_dict)
262 downloaded = ydl.downloaded_info_dicts[0]
263 self.assertEqual(downloaded['format_id'], f1['format_id'])
265 def test_format_filtering(self):
267 {'format_id': 'A', 'filesize': 500, 'width': 1000},
268 {'format_id': 'B', 'filesize': 1000, 'width': 500},
269 {'format_id': 'C', 'filesize': 1000, 'width': 400},
270 {'format_id': 'D', 'filesize': 2000, 'width': 600},
271 {'format_id': 'E', 'filesize': 3000},
273 {'format_id': 'G', 'filesize': 1000000},
276 f['url'] = 'http://_/'
278 info_dict = _make_result(formats)
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'], 'D')
285 ydl = YDL({'format': 'best[filesize<=3000]'})
286 ydl.process_ie_result(info_dict)
287 downloaded = ydl.downloaded_info_dicts[0]
288 self.assertEqual(downloaded['format_id'], 'E')
290 ydl = YDL({'format': 'best[filesize <= ? 3000]'})
291 ydl.process_ie_result(info_dict)
292 downloaded = ydl.downloaded_info_dicts[0]
293 self.assertEqual(downloaded['format_id'], 'F')
295 ydl = YDL({'format': 'best [filesize = 1000] [width>450]'})
296 ydl.process_ie_result(info_dict)
297 downloaded = ydl.downloaded_info_dicts[0]
298 self.assertEqual(downloaded['format_id'], 'B')
300 ydl = YDL({'format': 'best [filesize = 1000] [width!=450]'})
301 ydl.process_ie_result(info_dict)
302 downloaded = ydl.downloaded_info_dicts[0]
303 self.assertEqual(downloaded['format_id'], 'C')
305 ydl = YDL({'format': '[filesize>?1]'})
306 ydl.process_ie_result(info_dict)
307 downloaded = ydl.downloaded_info_dicts[0]
308 self.assertEqual(downloaded['format_id'], 'G')
310 ydl = YDL({'format': '[filesize<1M]'})
311 ydl.process_ie_result(info_dict)
312 downloaded = ydl.downloaded_info_dicts[0]
313 self.assertEqual(downloaded['format_id'], 'E')
315 ydl = YDL({'format': '[filesize<1MiB]'})
316 ydl.process_ie_result(info_dict)
317 downloaded = ydl.downloaded_info_dicts[0]
318 self.assertEqual(downloaded['format_id'], 'G')
320 ydl = YDL({'format': 'all[width>=400][width<=600]'})
321 ydl.process_ie_result(info_dict)
322 downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
323 self.assertEqual(downloaded_ids, ['B', 'C', 'D'])
326 class TestYoutubeDL(unittest.TestCase):
327 def test_subtitles(self):
328 def s_formats(lang, autocaption=False):
331 'url': 'http://localhost/video.%s.%s' % (lang, ext),
332 '_auto': autocaption,
333 } for ext in ['vtt', 'srt', 'ass']]
334 subtitles = dict((l, s_formats(l)) for l in ['en', 'fr', 'es'])
335 auto_captions = dict((l, s_formats(l, True)) for l in ['it', 'pt', 'es'])
339 'url': 'http://localhost/video.mp4',
340 'subtitles': subtitles,
341 'automatic_captions': auto_captions,
345 def get_info(params={}):
346 params.setdefault('simulate', True)
348 ydl.report_warning = lambda *args, **kargs: None
349 return ydl.process_video_result(info_dict, download=False)
352 self.assertFalse(result.get('requested_subtitles'))
353 self.assertEqual(result['subtitles'], subtitles)
354 self.assertEqual(result['automatic_captions'], auto_captions)
356 result = get_info({'writesubtitles': True})
357 subs = result['requested_subtitles']
358 self.assertTrue(subs)
359 self.assertEqual(set(subs.keys()), set(['en']))
360 self.assertTrue(subs['en'].get('data') is None)
361 self.assertEqual(subs['en']['ext'], 'ass')
363 result = get_info({'writesubtitles': True, 'subtitlesformat': 'foo/srt'})
364 subs = result['requested_subtitles']
365 self.assertEqual(subs['en']['ext'], 'srt')
367 result = get_info({'writesubtitles': True, 'subtitleslangs': ['es', 'fr', 'it']})
368 subs = result['requested_subtitles']
369 self.assertTrue(subs)
370 self.assertEqual(set(subs.keys()), set(['es', 'fr']))
372 result = get_info({'writesubtitles': True, 'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
373 subs = result['requested_subtitles']
374 self.assertTrue(subs)
375 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
376 self.assertFalse(subs['es']['_auto'])
377 self.assertTrue(subs['pt']['_auto'])
379 result = get_info({'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
380 subs = result['requested_subtitles']
381 self.assertTrue(subs)
382 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
383 self.assertTrue(subs['es']['_auto'])
384 self.assertTrue(subs['pt']['_auto'])
386 def test_add_extra_info(self):
392 'playlist': 'funny videos',
394 YDL.add_extra_info(test_dict, extra_info)
395 self.assertEqual(test_dict['extractor'], 'Foo')
396 self.assertEqual(test_dict['playlist'], 'funny videos')
398 def test_prepare_filename(self):
406 ydl = YoutubeDL({'outtmpl': templ})
407 return ydl.prepare_filename(info)
408 self.assertEqual(fname('%(id)s.%(ext)s'), '1234.mp4')
409 self.assertEqual(fname('%(id)s-%(width)s.%(ext)s'), '1234-NA.mp4')
410 # Replace missing fields with 'NA'
411 self.assertEqual(fname('%(uploader_date)s-%(id)s.%(ext)s'), 'NA-1234.mp4')
413 def test_format_note(self):
415 self.assertEqual(ydl._format_note({}), '')
416 assertRegexpMatches(self, ydl._format_note({
420 def test_postprocessors(self):
421 filename = 'post-processor-testfile.mp4'
422 audiofile = filename + '.mp3'
424 class SimplePP(PostProcessor):
426 with open(audiofile, 'wt') as f:
428 return [info['filepath']], info
430 def run_pp(params, PP):
431 with open(filename, 'wt') as f:
433 ydl = YoutubeDL(params)
434 ydl.add_post_processor(PP())
435 ydl.post_process(filename, {'filepath': filename})
437 run_pp({'keepvideo': True}, SimplePP)
438 self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
439 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
443 run_pp({'keepvideo': False}, SimplePP)
444 self.assertFalse(os.path.exists(filename), '%s exists' % filename)
445 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
448 class ModifierPP(PostProcessor):
450 with open(info['filepath'], 'wt') as f:
454 run_pp({'keepvideo': False}, ModifierPP)
455 self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
458 def test_match_filter(self):
459 class FilterYDL(YDL):
460 def __init__(self, *args, **kwargs):
461 super(FilterYDL, self).__init__(*args, **kwargs)
462 self.params['simulate'] = True
464 def process_info(self, info_dict):
465 super(YDL, self).process_info(info_dict)
467 def _match_entry(self, info_dict, incomplete):
468 res = super(FilterYDL, self)._match_entry(info_dict, incomplete)
470 self.downloaded_info_dicts.append(info_dict)
479 'filesize': 10 * 1024,
487 'description': 'foo',
488 'filesize': 5 * 1024,
490 videos = [first, second]
492 def get_videos(filter_=None):
493 ydl = FilterYDL({'match_filter': filter_})
495 ydl.process_ie_result(v, download=True)
496 return [v['id'] for v in ydl.downloaded_info_dicts]
499 self.assertEqual(res, ['1', '2'])
505 return 'Video id is not 1'
507 self.assertEqual(res, ['1'])
509 f = match_filter_func('duration < 30')
511 self.assertEqual(res, ['2'])
513 f = match_filter_func('description = foo')
515 self.assertEqual(res, ['2'])
517 f = match_filter_func('description =? foo')
519 self.assertEqual(res, ['1', '2'])
521 f = match_filter_func('filesize > 5KiB')
523 self.assertEqual(res, ['1'])
525 def test_playlist_items_selection(self):
528 'title': compat_str(i),
530 } for i in range(1, 5)]
535 'extractor': 'test:playlist',
536 'extractor_key': 'test:playlist',
537 'webpage_url': 'http://example.com',
542 # make a copy because the dictionary can be modified
543 ydl.process_ie_result(playlist.copy())
544 return [int(v['id']) for v in ydl.downloaded_info_dicts]
547 self.assertEqual(result, [1, 2, 3, 4])
549 result = get_ids({'playlistend': 10})
550 self.assertEqual(result, [1, 2, 3, 4])
552 result = get_ids({'playlistend': 2})
553 self.assertEqual(result, [1, 2])
555 result = get_ids({'playliststart': 10})
556 self.assertEqual(result, [])
558 result = get_ids({'playliststart': 2})
559 self.assertEqual(result, [2, 3, 4])
561 result = get_ids({'playlist_items': '2-4'})
562 self.assertEqual(result, [2, 3, 4])
564 result = get_ids({'playlist_items': '2,4'})
565 self.assertEqual(result, [2, 4])
567 result = get_ids({'playlist_items': '10'})
568 self.assertEqual(result, [])
571 if __name__ == '__main__':