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.extractor import YoutubeIE
16 from youtube_dl.postprocessor.common import PostProcessor
17 from youtube_dl.utils import match_filter_func
19 TEST_URL = 'http://localhost/sample.mp4'
23 def __init__(self, *args, **kwargs):
24 super(YDL, self).__init__(*args, **kwargs)
25 self.downloaded_info_dicts = []
28 def process_info(self, info_dict):
29 self.downloaded_info_dicts.append(info_dict)
31 def to_screen(self, msg):
35 def _make_result(formats, **kwargs):
39 'title': 'testttitle',
40 'extractor': 'testex',
46 class TestFormatSelection(unittest.TestCase):
47 def test_prefer_free_formats(self):
48 # Same resolution => download webm
50 ydl.params['prefer_free_formats'] = True
52 {'ext': 'webm', 'height': 460, 'url': TEST_URL},
53 {'ext': 'mp4', 'height': 460, 'url': TEST_URL},
55 info_dict = _make_result(formats)
57 yie._sort_formats(info_dict['formats'])
58 ydl.process_ie_result(info_dict)
59 downloaded = ydl.downloaded_info_dicts[0]
60 self.assertEqual(downloaded['ext'], 'webm')
62 # Different resolution => download best quality (mp4)
64 ydl.params['prefer_free_formats'] = True
66 {'ext': 'webm', 'height': 720, 'url': TEST_URL},
67 {'ext': 'mp4', 'height': 1080, 'url': TEST_URL},
69 info_dict['formats'] = formats
71 yie._sort_formats(info_dict['formats'])
72 ydl.process_ie_result(info_dict)
73 downloaded = ydl.downloaded_info_dicts[0]
74 self.assertEqual(downloaded['ext'], 'mp4')
76 # No prefer_free_formats => prefer mp4 and flv for greater compatibility
78 ydl.params['prefer_free_formats'] = False
80 {'ext': 'webm', 'height': 720, 'url': TEST_URL},
81 {'ext': 'mp4', 'height': 720, 'url': TEST_URL},
82 {'ext': 'flv', 'height': 720, 'url': TEST_URL},
84 info_dict['formats'] = formats
86 yie._sort_formats(info_dict['formats'])
87 ydl.process_ie_result(info_dict)
88 downloaded = ydl.downloaded_info_dicts[0]
89 self.assertEqual(downloaded['ext'], 'mp4')
92 ydl.params['prefer_free_formats'] = False
94 {'ext': 'flv', 'height': 720, 'url': TEST_URL},
95 {'ext': 'webm', 'height': 720, 'url': TEST_URL},
97 info_dict['formats'] = formats
99 yie._sort_formats(info_dict['formats'])
100 ydl.process_ie_result(info_dict)
101 downloaded = ydl.downloaded_info_dicts[0]
102 self.assertEqual(downloaded['ext'], 'flv')
104 def test_format_limit(self):
106 {'format_id': 'meh', 'url': 'http://example.com/meh', 'preference': 1},
107 {'format_id': 'good', 'url': 'http://example.com/good', 'preference': 2},
108 {'format_id': 'great', 'url': 'http://example.com/great', 'preference': 3},
109 {'format_id': 'excellent', 'url': 'http://example.com/exc', 'preference': 4},
111 info_dict = _make_result(formats)
114 ydl.process_ie_result(info_dict)
115 downloaded = ydl.downloaded_info_dicts[0]
116 self.assertEqual(downloaded['format_id'], 'excellent')
118 ydl = YDL({'format_limit': 'good'})
119 assert ydl.params['format_limit'] == 'good'
120 ydl.process_ie_result(info_dict.copy())
121 downloaded = ydl.downloaded_info_dicts[0]
122 self.assertEqual(downloaded['format_id'], 'good')
124 ydl = YDL({'format_limit': 'great', 'format': 'all'})
125 ydl.process_ie_result(info_dict.copy())
126 self.assertEqual(ydl.downloaded_info_dicts[0]['format_id'], 'meh')
127 self.assertEqual(ydl.downloaded_info_dicts[1]['format_id'], 'good')
128 self.assertEqual(ydl.downloaded_info_dicts[2]['format_id'], 'great')
129 self.assertTrue('3' in ydl.msgs[0])
132 ydl.params['format_limit'] = 'excellent'
133 ydl.process_ie_result(info_dict.copy())
134 downloaded = ydl.downloaded_info_dicts[0]
135 self.assertEqual(downloaded['format_id'], 'excellent')
137 def test_format_selection(self):
139 {'format_id': '35', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL},
140 {'format_id': '45', 'ext': 'webm', 'preference': 2, 'url': TEST_URL},
141 {'format_id': '47', 'ext': 'webm', 'preference': 3, 'url': TEST_URL},
142 {'format_id': '2', 'ext': 'flv', 'preference': 4, 'url': TEST_URL},
144 info_dict = _make_result(formats)
146 ydl = YDL({'format': '20/47'})
147 ydl.process_ie_result(info_dict.copy())
148 downloaded = ydl.downloaded_info_dicts[0]
149 self.assertEqual(downloaded['format_id'], '47')
151 ydl = YDL({'format': '20/71/worst'})
152 ydl.process_ie_result(info_dict.copy())
153 downloaded = ydl.downloaded_info_dicts[0]
154 self.assertEqual(downloaded['format_id'], '35')
157 ydl.process_ie_result(info_dict.copy())
158 downloaded = ydl.downloaded_info_dicts[0]
159 self.assertEqual(downloaded['format_id'], '2')
161 ydl = YDL({'format': 'webm/mp4'})
162 ydl.process_ie_result(info_dict.copy())
163 downloaded = ydl.downloaded_info_dicts[0]
164 self.assertEqual(downloaded['format_id'], '47')
166 ydl = YDL({'format': '3gp/40/mp4'})
167 ydl.process_ie_result(info_dict.copy())
168 downloaded = ydl.downloaded_info_dicts[0]
169 self.assertEqual(downloaded['format_id'], '35')
171 def test_format_selection_audio(self):
173 {'format_id': 'audio-low', 'ext': 'webm', 'preference': 1, 'vcodec': 'none', 'url': TEST_URL},
174 {'format_id': 'audio-mid', 'ext': 'webm', 'preference': 2, 'vcodec': 'none', 'url': TEST_URL},
175 {'format_id': 'audio-high', 'ext': 'flv', 'preference': 3, 'vcodec': 'none', 'url': TEST_URL},
176 {'format_id': 'vid', 'ext': 'mp4', 'preference': 4, 'url': TEST_URL},
178 info_dict = _make_result(formats)
180 ydl = YDL({'format': 'bestaudio'})
181 ydl.process_ie_result(info_dict.copy())
182 downloaded = ydl.downloaded_info_dicts[0]
183 self.assertEqual(downloaded['format_id'], 'audio-high')
185 ydl = YDL({'format': 'worstaudio'})
186 ydl.process_ie_result(info_dict.copy())
187 downloaded = ydl.downloaded_info_dicts[0]
188 self.assertEqual(downloaded['format_id'], 'audio-low')
191 {'format_id': 'vid-low', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL},
192 {'format_id': 'vid-high', 'ext': 'mp4', 'preference': 2, 'url': TEST_URL},
194 info_dict = _make_result(formats)
196 ydl = YDL({'format': 'bestaudio/worstaudio/best'})
197 ydl.process_ie_result(info_dict.copy())
198 downloaded = ydl.downloaded_info_dicts[0]
199 self.assertEqual(downloaded['format_id'], 'vid-high')
201 def test_format_selection_audio_exts(self):
203 {'format_id': 'mp3-64', 'ext': 'mp3', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
204 {'format_id': 'ogg-64', 'ext': 'ogg', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
205 {'format_id': 'aac-64', 'ext': 'aac', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
206 {'format_id': 'mp3-32', 'ext': 'mp3', 'abr': 32, 'url': 'http://_', 'vcodec': 'none'},
207 {'format_id': 'aac-32', 'ext': 'aac', 'abr': 32, 'url': 'http://_', 'vcodec': 'none'},
210 info_dict = _make_result(formats)
211 ydl = YDL({'format': 'best'})
213 ie._sort_formats(info_dict['formats'])
214 ydl.process_ie_result(copy.deepcopy(info_dict))
215 downloaded = ydl.downloaded_info_dicts[0]
216 self.assertEqual(downloaded['format_id'], 'aac-64')
218 ydl = YDL({'format': 'mp3'})
220 ie._sort_formats(info_dict['formats'])
221 ydl.process_ie_result(copy.deepcopy(info_dict))
222 downloaded = ydl.downloaded_info_dicts[0]
223 self.assertEqual(downloaded['format_id'], 'mp3-64')
225 ydl = YDL({'prefer_free_formats': True})
227 ie._sort_formats(info_dict['formats'])
228 ydl.process_ie_result(copy.deepcopy(info_dict))
229 downloaded = ydl.downloaded_info_dicts[0]
230 self.assertEqual(downloaded['format_id'], 'ogg-64')
232 def test_format_selection_video(self):
234 {'format_id': 'dash-video-low', 'ext': 'mp4', 'preference': 1, 'acodec': 'none', 'url': TEST_URL},
235 {'format_id': 'dash-video-high', 'ext': 'mp4', 'preference': 2, 'acodec': 'none', 'url': TEST_URL},
236 {'format_id': 'vid', 'ext': 'mp4', 'preference': 3, 'url': TEST_URL},
238 info_dict = _make_result(formats)
240 ydl = YDL({'format': 'bestvideo'})
241 ydl.process_ie_result(info_dict.copy())
242 downloaded = ydl.downloaded_info_dicts[0]
243 self.assertEqual(downloaded['format_id'], 'dash-video-high')
245 ydl = YDL({'format': 'worstvideo'})
246 ydl.process_ie_result(info_dict.copy())
247 downloaded = ydl.downloaded_info_dicts[0]
248 self.assertEqual(downloaded['format_id'], 'dash-video-low')
250 def test_youtube_format_selection(self):
252 '38', '37', '46', '22', '45', '35', '44', '18', '34', '43', '6', '5', '36', '17', '13',
253 # Apple HTTP Live Streaming
254 '96', '95', '94', '93', '92', '132', '151',
256 '85', '84', '102', '83', '101', '82', '100',
258 '137', '248', '136', '247', '135', '246',
259 '245', '244', '134', '243', '133', '242', '160',
261 '141', '172', '140', '171', '139',
264 for f1id, f2id in zip(order, order[1:]):
265 f1 = YoutubeIE._formats[f1id].copy()
266 f1['format_id'] = f1id
267 f1['url'] = 'url:' + f1id
268 f2 = YoutubeIE._formats[f2id].copy()
269 f2['format_id'] = f2id
270 f2['url'] = 'url:' + f2id
272 info_dict = _make_result([f1, f2], extractor='youtube')
275 yie._sort_formats(info_dict['formats'])
276 ydl.process_ie_result(info_dict)
277 downloaded = ydl.downloaded_info_dicts[0]
278 self.assertEqual(downloaded['format_id'], f1id)
280 info_dict = _make_result([f2, f1], extractor='youtube')
283 yie._sort_formats(info_dict['formats'])
284 ydl.process_ie_result(info_dict)
285 downloaded = ydl.downloaded_info_dicts[0]
286 self.assertEqual(downloaded['format_id'], f1id)
288 def test_format_filtering(self):
290 {'format_id': 'A', 'filesize': 500, 'width': 1000},
291 {'format_id': 'B', 'filesize': 1000, 'width': 500},
292 {'format_id': 'C', 'filesize': 1000, 'width': 400},
293 {'format_id': 'D', 'filesize': 2000, 'width': 600},
294 {'format_id': 'E', 'filesize': 3000},
296 {'format_id': 'G', 'filesize': 1000000},
299 f['url'] = 'http://_/'
301 info_dict = _make_result(formats)
303 ydl = YDL({'format': 'best[filesize<3000]'})
304 ydl.process_ie_result(info_dict)
305 downloaded = ydl.downloaded_info_dicts[0]
306 self.assertEqual(downloaded['format_id'], 'D')
308 ydl = YDL({'format': 'best[filesize<=3000]'})
309 ydl.process_ie_result(info_dict)
310 downloaded = ydl.downloaded_info_dicts[0]
311 self.assertEqual(downloaded['format_id'], 'E')
313 ydl = YDL({'format': 'best[filesize <= ? 3000]'})
314 ydl.process_ie_result(info_dict)
315 downloaded = ydl.downloaded_info_dicts[0]
316 self.assertEqual(downloaded['format_id'], 'F')
318 ydl = YDL({'format': 'best [filesize = 1000] [width>450]'})
319 ydl.process_ie_result(info_dict)
320 downloaded = ydl.downloaded_info_dicts[0]
321 self.assertEqual(downloaded['format_id'], 'B')
323 ydl = YDL({'format': 'best [filesize = 1000] [width!=450]'})
324 ydl.process_ie_result(info_dict)
325 downloaded = ydl.downloaded_info_dicts[0]
326 self.assertEqual(downloaded['format_id'], 'C')
328 ydl = YDL({'format': '[filesize>?1]'})
329 ydl.process_ie_result(info_dict)
330 downloaded = ydl.downloaded_info_dicts[0]
331 self.assertEqual(downloaded['format_id'], 'G')
333 ydl = YDL({'format': '[filesize<1M]'})
334 ydl.process_ie_result(info_dict)
335 downloaded = ydl.downloaded_info_dicts[0]
336 self.assertEqual(downloaded['format_id'], 'E')
338 ydl = YDL({'format': '[filesize<1MiB]'})
339 ydl.process_ie_result(info_dict)
340 downloaded = ydl.downloaded_info_dicts[0]
341 self.assertEqual(downloaded['format_id'], 'G')
344 class TestYoutubeDL(unittest.TestCase):
345 def test_subtitles(self):
346 def s_formats(lang, autocaption=False):
349 'url': 'http://localhost/video.%s.%s' % (lang, ext),
350 '_auto': autocaption,
351 } for ext in ['vtt', 'srt', 'ass']]
352 subtitles = dict((l, s_formats(l)) for l in ['en', 'fr', 'es'])
353 auto_captions = dict((l, s_formats(l, True)) for l in ['it', 'pt', 'es'])
357 'url': 'http://localhost/video.mp4',
358 'subtitles': subtitles,
359 'automatic_captions': auto_captions,
363 def get_info(params={}):
364 params.setdefault('simulate', True)
366 ydl.report_warning = lambda *args, **kargs: None
367 return ydl.process_video_result(info_dict, download=False)
370 self.assertFalse(result.get('requested_subtitles'))
371 self.assertEqual(result['subtitles'], subtitles)
372 self.assertEqual(result['automatic_captions'], auto_captions)
374 result = get_info({'writesubtitles': True})
375 subs = result['requested_subtitles']
376 self.assertTrue(subs)
377 self.assertEqual(set(subs.keys()), set(['en']))
378 self.assertTrue(subs['en'].get('data') is None)
379 self.assertEqual(subs['en']['ext'], 'ass')
381 result = get_info({'writesubtitles': True, 'subtitlesformat': 'foo/srt'})
382 subs = result['requested_subtitles']
383 self.assertEqual(subs['en']['ext'], 'srt')
385 result = get_info({'writesubtitles': True, 'subtitleslangs': ['es', 'fr', 'it']})
386 subs = result['requested_subtitles']
387 self.assertTrue(subs)
388 self.assertEqual(set(subs.keys()), set(['es', 'fr']))
390 result = get_info({'writesubtitles': True, 'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
391 subs = result['requested_subtitles']
392 self.assertTrue(subs)
393 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
394 self.assertFalse(subs['es']['_auto'])
395 self.assertTrue(subs['pt']['_auto'])
397 result = get_info({'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
398 subs = result['requested_subtitles']
399 self.assertTrue(subs)
400 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
401 self.assertTrue(subs['es']['_auto'])
402 self.assertTrue(subs['pt']['_auto'])
404 def test_add_extra_info(self):
410 'playlist': 'funny videos',
412 YDL.add_extra_info(test_dict, extra_info)
413 self.assertEqual(test_dict['extractor'], 'Foo')
414 self.assertEqual(test_dict['playlist'], 'funny videos')
416 def test_prepare_filename(self):
424 ydl = YoutubeDL({'outtmpl': templ})
425 return ydl.prepare_filename(info)
426 self.assertEqual(fname('%(id)s.%(ext)s'), '1234.mp4')
427 self.assertEqual(fname('%(id)s-%(width)s.%(ext)s'), '1234-NA.mp4')
428 # Replace missing fields with 'NA'
429 self.assertEqual(fname('%(uploader_date)s-%(id)s.%(ext)s'), 'NA-1234.mp4')
431 def test_format_note(self):
433 self.assertEqual(ydl._format_note({}), '')
434 assertRegexpMatches(self, ydl._format_note({
438 def test_postprocessors(self):
439 filename = 'post-processor-testfile.mp4'
440 audiofile = filename + '.mp3'
442 class SimplePP(PostProcessor):
444 with open(audiofile, 'wt') as f:
446 return [info['filepath']], info
448 def run_pp(params, PP):
449 with open(filename, 'wt') as f:
451 ydl = YoutubeDL(params)
452 ydl.add_post_processor(PP())
453 ydl.post_process(filename, {'filepath': filename})
455 run_pp({'keepvideo': True}, SimplePP)
456 self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
457 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
461 run_pp({'keepvideo': False}, SimplePP)
462 self.assertFalse(os.path.exists(filename), '%s exists' % filename)
463 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
466 class ModifierPP(PostProcessor):
468 with open(info['filepath'], 'wt') as f:
472 run_pp({'keepvideo': False}, ModifierPP)
473 self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
476 def test_match_filter(self):
477 class FilterYDL(YDL):
478 def __init__(self, *args, **kwargs):
479 super(FilterYDL, self).__init__(*args, **kwargs)
480 self.params['simulate'] = True
482 def process_info(self, info_dict):
483 super(YDL, self).process_info(info_dict)
485 def _match_entry(self, info_dict, incomplete):
486 res = super(FilterYDL, self)._match_entry(info_dict, incomplete)
488 self.downloaded_info_dicts.append(info_dict)
497 'filesize': 10 * 1024,
505 'description': 'foo',
506 'filesize': 5 * 1024,
508 videos = [first, second]
510 def get_videos(filter_=None):
511 ydl = FilterYDL({'match_filter': filter_})
513 ydl.process_ie_result(v, download=True)
514 return [v['id'] for v in ydl.downloaded_info_dicts]
517 self.assertEqual(res, ['1', '2'])
523 return 'Video id is not 1'
525 self.assertEqual(res, ['1'])
527 f = match_filter_func('duration < 30')
529 self.assertEqual(res, ['2'])
531 f = match_filter_func('description = foo')
533 self.assertEqual(res, ['2'])
535 f = match_filter_func('description =? foo')
537 self.assertEqual(res, ['1', '2'])
539 f = match_filter_func('filesize > 5KiB')
541 self.assertEqual(res, ['1'])
544 if __name__ == '__main__':