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')
321 class TestYoutubeDL(unittest.TestCase):
322 def test_subtitles(self):
323 def s_formats(lang, autocaption=False):
326 'url': 'http://localhost/video.%s.%s' % (lang, ext),
327 '_auto': autocaption,
328 } for ext in ['vtt', 'srt', 'ass']]
329 subtitles = dict((l, s_formats(l)) for l in ['en', 'fr', 'es'])
330 auto_captions = dict((l, s_formats(l, True)) for l in ['it', 'pt', 'es'])
334 'url': 'http://localhost/video.mp4',
335 'subtitles': subtitles,
336 'automatic_captions': auto_captions,
340 def get_info(params={}):
341 params.setdefault('simulate', True)
343 ydl.report_warning = lambda *args, **kargs: None
344 return ydl.process_video_result(info_dict, download=False)
347 self.assertFalse(result.get('requested_subtitles'))
348 self.assertEqual(result['subtitles'], subtitles)
349 self.assertEqual(result['automatic_captions'], auto_captions)
351 result = get_info({'writesubtitles': True})
352 subs = result['requested_subtitles']
353 self.assertTrue(subs)
354 self.assertEqual(set(subs.keys()), set(['en']))
355 self.assertTrue(subs['en'].get('data') is None)
356 self.assertEqual(subs['en']['ext'], 'ass')
358 result = get_info({'writesubtitles': True, 'subtitlesformat': 'foo/srt'})
359 subs = result['requested_subtitles']
360 self.assertEqual(subs['en']['ext'], 'srt')
362 result = get_info({'writesubtitles': True, 'subtitleslangs': ['es', 'fr', 'it']})
363 subs = result['requested_subtitles']
364 self.assertTrue(subs)
365 self.assertEqual(set(subs.keys()), set(['es', 'fr']))
367 result = get_info({'writesubtitles': True, 'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
368 subs = result['requested_subtitles']
369 self.assertTrue(subs)
370 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
371 self.assertFalse(subs['es']['_auto'])
372 self.assertTrue(subs['pt']['_auto'])
374 result = get_info({'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
375 subs = result['requested_subtitles']
376 self.assertTrue(subs)
377 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
378 self.assertTrue(subs['es']['_auto'])
379 self.assertTrue(subs['pt']['_auto'])
381 def test_add_extra_info(self):
387 'playlist': 'funny videos',
389 YDL.add_extra_info(test_dict, extra_info)
390 self.assertEqual(test_dict['extractor'], 'Foo')
391 self.assertEqual(test_dict['playlist'], 'funny videos')
393 def test_prepare_filename(self):
401 ydl = YoutubeDL({'outtmpl': templ})
402 return ydl.prepare_filename(info)
403 self.assertEqual(fname('%(id)s.%(ext)s'), '1234.mp4')
404 self.assertEqual(fname('%(id)s-%(width)s.%(ext)s'), '1234-NA.mp4')
405 # Replace missing fields with 'NA'
406 self.assertEqual(fname('%(uploader_date)s-%(id)s.%(ext)s'), 'NA-1234.mp4')
408 def test_format_note(self):
410 self.assertEqual(ydl._format_note({}), '')
411 assertRegexpMatches(self, ydl._format_note({
415 def test_postprocessors(self):
416 filename = 'post-processor-testfile.mp4'
417 audiofile = filename + '.mp3'
419 class SimplePP(PostProcessor):
421 with open(audiofile, 'wt') as f:
423 return [info['filepath']], info
425 def run_pp(params, PP):
426 with open(filename, 'wt') as f:
428 ydl = YoutubeDL(params)
429 ydl.add_post_processor(PP())
430 ydl.post_process(filename, {'filepath': filename})
432 run_pp({'keepvideo': True}, SimplePP)
433 self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
434 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
438 run_pp({'keepvideo': False}, SimplePP)
439 self.assertFalse(os.path.exists(filename), '%s exists' % filename)
440 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
443 class ModifierPP(PostProcessor):
445 with open(info['filepath'], 'wt') as f:
449 run_pp({'keepvideo': False}, ModifierPP)
450 self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
453 def test_match_filter(self):
454 class FilterYDL(YDL):
455 def __init__(self, *args, **kwargs):
456 super(FilterYDL, self).__init__(*args, **kwargs)
457 self.params['simulate'] = True
459 def process_info(self, info_dict):
460 super(YDL, self).process_info(info_dict)
462 def _match_entry(self, info_dict, incomplete):
463 res = super(FilterYDL, self)._match_entry(info_dict, incomplete)
465 self.downloaded_info_dicts.append(info_dict)
474 'filesize': 10 * 1024,
482 'description': 'foo',
483 'filesize': 5 * 1024,
485 videos = [first, second]
487 def get_videos(filter_=None):
488 ydl = FilterYDL({'match_filter': filter_})
490 ydl.process_ie_result(v, download=True)
491 return [v['id'] for v in ydl.downloaded_info_dicts]
494 self.assertEqual(res, ['1', '2'])
500 return 'Video id is not 1'
502 self.assertEqual(res, ['1'])
504 f = match_filter_func('duration < 30')
506 self.assertEqual(res, ['2'])
508 f = match_filter_func('description = foo')
510 self.assertEqual(res, ['2'])
512 f = match_filter_func('description =? foo')
514 self.assertEqual(res, ['1', '2'])
516 f = match_filter_func('filesize > 5KiB')
518 self.assertEqual(res, ['1'])
520 def test_playlist_items_selection(self):
523 'title': compat_str(i),
525 } for i in range(1, 5)]
530 'extractor': 'test:playlist',
531 'extractor_key': 'test:playlist',
532 'webpage_url': 'http://example.com',
537 # make a copy because the dictionary can be modified
538 ydl.process_ie_result(playlist.copy())
539 return [int(v['id']) for v in ydl.downloaded_info_dicts]
542 self.assertEqual(result, [1, 2, 3, 4])
544 result = get_ids({'playlistend': 10})
545 self.assertEqual(result, [1, 2, 3, 4])
547 result = get_ids({'playlistend': 2})
548 self.assertEqual(result, [1, 2])
550 result = get_ids({'playliststart': 10})
551 self.assertEqual(result, [])
553 result = get_ids({'playliststart': 2})
554 self.assertEqual(result, [2, 3, 4])
556 result = get_ids({'playlist_items': '2-4'})
557 self.assertEqual(result, [2, 3, 4])
559 result = get_ids({'playlist_items': '2,4'})
560 self.assertEqual(result, [2, 4])
562 result = get_ids({'playlist_items': '10'})
563 self.assertEqual(result, [])
566 if __name__ == '__main__':