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
18 TEST_URL = 'http://localhost/sample.mp4'
22 def __init__(self, *args, **kwargs):
23 super(YDL, self).__init__(*args, **kwargs)
24 self.downloaded_info_dicts = []
27 def process_info(self, info_dict):
28 self.downloaded_info_dicts.append(info_dict)
30 def to_screen(self, msg):
34 def _make_result(formats, **kwargs):
38 'title': 'testttitle',
39 'extractor': 'testex',
45 class TestFormatSelection(unittest.TestCase):
46 def test_prefer_free_formats(self):
47 # Same resolution => download webm
49 ydl.params['prefer_free_formats'] = True
51 {'ext': 'webm', 'height': 460, 'url': TEST_URL},
52 {'ext': 'mp4', 'height': 460, 'url': TEST_URL},
54 info_dict = _make_result(formats)
56 yie._sort_formats(info_dict['formats'])
57 ydl.process_ie_result(info_dict)
58 downloaded = ydl.downloaded_info_dicts[0]
59 self.assertEqual(downloaded['ext'], 'webm')
61 # Different resolution => download best quality (mp4)
63 ydl.params['prefer_free_formats'] = True
65 {'ext': 'webm', 'height': 720, 'url': TEST_URL},
66 {'ext': 'mp4', 'height': 1080, 'url': TEST_URL},
68 info_dict['formats'] = formats
70 yie._sort_formats(info_dict['formats'])
71 ydl.process_ie_result(info_dict)
72 downloaded = ydl.downloaded_info_dicts[0]
73 self.assertEqual(downloaded['ext'], 'mp4')
75 # No prefer_free_formats => prefer mp4 and flv for greater compatibility
77 ydl.params['prefer_free_formats'] = False
79 {'ext': 'webm', 'height': 720, 'url': TEST_URL},
80 {'ext': 'mp4', 'height': 720, 'url': TEST_URL},
81 {'ext': 'flv', 'height': 720, 'url': TEST_URL},
83 info_dict['formats'] = formats
85 yie._sort_formats(info_dict['formats'])
86 ydl.process_ie_result(info_dict)
87 downloaded = ydl.downloaded_info_dicts[0]
88 self.assertEqual(downloaded['ext'], 'mp4')
91 ydl.params['prefer_free_formats'] = False
93 {'ext': 'flv', 'height': 720, 'url': TEST_URL},
94 {'ext': 'webm', 'height': 720, 'url': TEST_URL},
96 info_dict['formats'] = formats
98 yie._sort_formats(info_dict['formats'])
99 ydl.process_ie_result(info_dict)
100 downloaded = ydl.downloaded_info_dicts[0]
101 self.assertEqual(downloaded['ext'], 'flv')
103 def test_format_limit(self):
105 {'format_id': 'meh', 'url': 'http://example.com/meh', 'preference': 1},
106 {'format_id': 'good', 'url': 'http://example.com/good', 'preference': 2},
107 {'format_id': 'great', 'url': 'http://example.com/great', 'preference': 3},
108 {'format_id': 'excellent', 'url': 'http://example.com/exc', 'preference': 4},
110 info_dict = _make_result(formats)
113 ydl.process_ie_result(info_dict)
114 downloaded = ydl.downloaded_info_dicts[0]
115 self.assertEqual(downloaded['format_id'], 'excellent')
117 ydl = YDL({'format_limit': 'good'})
118 assert ydl.params['format_limit'] == 'good'
119 ydl.process_ie_result(info_dict.copy())
120 downloaded = ydl.downloaded_info_dicts[0]
121 self.assertEqual(downloaded['format_id'], 'good')
123 ydl = YDL({'format_limit': 'great', 'format': 'all'})
124 ydl.process_ie_result(info_dict.copy())
125 self.assertEqual(ydl.downloaded_info_dicts[0]['format_id'], 'meh')
126 self.assertEqual(ydl.downloaded_info_dicts[1]['format_id'], 'good')
127 self.assertEqual(ydl.downloaded_info_dicts[2]['format_id'], 'great')
128 self.assertTrue('3' in ydl.msgs[0])
131 ydl.params['format_limit'] = 'excellent'
132 ydl.process_ie_result(info_dict.copy())
133 downloaded = ydl.downloaded_info_dicts[0]
134 self.assertEqual(downloaded['format_id'], 'excellent')
136 def test_format_selection(self):
138 {'format_id': '35', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL},
139 {'format_id': '45', 'ext': 'webm', 'preference': 2, 'url': TEST_URL},
140 {'format_id': '47', 'ext': 'webm', 'preference': 3, 'url': TEST_URL},
141 {'format_id': '2', 'ext': 'flv', 'preference': 4, 'url': TEST_URL},
143 info_dict = _make_result(formats)
145 ydl = YDL({'format': '20/47'})
146 ydl.process_ie_result(info_dict.copy())
147 downloaded = ydl.downloaded_info_dicts[0]
148 self.assertEqual(downloaded['format_id'], '47')
150 ydl = YDL({'format': '20/71/worst'})
151 ydl.process_ie_result(info_dict.copy())
152 downloaded = ydl.downloaded_info_dicts[0]
153 self.assertEqual(downloaded['format_id'], '35')
156 ydl.process_ie_result(info_dict.copy())
157 downloaded = ydl.downloaded_info_dicts[0]
158 self.assertEqual(downloaded['format_id'], '2')
160 ydl = YDL({'format': 'webm/mp4'})
161 ydl.process_ie_result(info_dict.copy())
162 downloaded = ydl.downloaded_info_dicts[0]
163 self.assertEqual(downloaded['format_id'], '47')
165 ydl = YDL({'format': '3gp/40/mp4'})
166 ydl.process_ie_result(info_dict.copy())
167 downloaded = ydl.downloaded_info_dicts[0]
168 self.assertEqual(downloaded['format_id'], '35')
170 def test_format_selection_audio(self):
172 {'format_id': 'audio-low', 'ext': 'webm', 'preference': 1, 'vcodec': 'none', 'url': TEST_URL},
173 {'format_id': 'audio-mid', 'ext': 'webm', 'preference': 2, 'vcodec': 'none', 'url': TEST_URL},
174 {'format_id': 'audio-high', 'ext': 'flv', 'preference': 3, 'vcodec': 'none', 'url': TEST_URL},
175 {'format_id': 'vid', 'ext': 'mp4', 'preference': 4, 'url': TEST_URL},
177 info_dict = _make_result(formats)
179 ydl = YDL({'format': 'bestaudio'})
180 ydl.process_ie_result(info_dict.copy())
181 downloaded = ydl.downloaded_info_dicts[0]
182 self.assertEqual(downloaded['format_id'], 'audio-high')
184 ydl = YDL({'format': 'worstaudio'})
185 ydl.process_ie_result(info_dict.copy())
186 downloaded = ydl.downloaded_info_dicts[0]
187 self.assertEqual(downloaded['format_id'], 'audio-low')
190 {'format_id': 'vid-low', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL},
191 {'format_id': 'vid-high', 'ext': 'mp4', 'preference': 2, 'url': TEST_URL},
193 info_dict = _make_result(formats)
195 ydl = YDL({'format': 'bestaudio/worstaudio/best'})
196 ydl.process_ie_result(info_dict.copy())
197 downloaded = ydl.downloaded_info_dicts[0]
198 self.assertEqual(downloaded['format_id'], 'vid-high')
200 def test_format_selection_audio_exts(self):
202 {'format_id': 'mp3-64', 'ext': 'mp3', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
203 {'format_id': 'ogg-64', 'ext': 'ogg', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
204 {'format_id': 'aac-64', 'ext': 'aac', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
205 {'format_id': 'mp3-32', 'ext': 'mp3', 'abr': 32, 'url': 'http://_', 'vcodec': 'none'},
206 {'format_id': 'aac-32', 'ext': 'aac', 'abr': 32, 'url': 'http://_', 'vcodec': 'none'},
209 info_dict = _make_result(formats)
210 ydl = YDL({'format': 'best'})
212 ie._sort_formats(info_dict['formats'])
213 ydl.process_ie_result(copy.deepcopy(info_dict))
214 downloaded = ydl.downloaded_info_dicts[0]
215 self.assertEqual(downloaded['format_id'], 'aac-64')
217 ydl = YDL({'format': 'mp3'})
219 ie._sort_formats(info_dict['formats'])
220 ydl.process_ie_result(copy.deepcopy(info_dict))
221 downloaded = ydl.downloaded_info_dicts[0]
222 self.assertEqual(downloaded['format_id'], 'mp3-64')
224 ydl = YDL({'prefer_free_formats': True})
226 ie._sort_formats(info_dict['formats'])
227 ydl.process_ie_result(copy.deepcopy(info_dict))
228 downloaded = ydl.downloaded_info_dicts[0]
229 self.assertEqual(downloaded['format_id'], 'ogg-64')
231 def test_format_selection_video(self):
233 {'format_id': 'dash-video-low', 'ext': 'mp4', 'preference': 1, 'acodec': 'none', 'url': TEST_URL},
234 {'format_id': 'dash-video-high', 'ext': 'mp4', 'preference': 2, 'acodec': 'none', 'url': TEST_URL},
235 {'format_id': 'vid', 'ext': 'mp4', 'preference': 3, 'url': TEST_URL},
237 info_dict = _make_result(formats)
239 ydl = YDL({'format': 'bestvideo'})
240 ydl.process_ie_result(info_dict.copy())
241 downloaded = ydl.downloaded_info_dicts[0]
242 self.assertEqual(downloaded['format_id'], 'dash-video-high')
244 ydl = YDL({'format': 'worstvideo'})
245 ydl.process_ie_result(info_dict.copy())
246 downloaded = ydl.downloaded_info_dicts[0]
247 self.assertEqual(downloaded['format_id'], 'dash-video-low')
249 def test_youtube_format_selection(self):
251 '38', '37', '46', '22', '45', '35', '44', '18', '34', '43', '6', '5', '36', '17', '13',
252 # Apple HTTP Live Streaming
253 '96', '95', '94', '93', '92', '132', '151',
255 '85', '84', '102', '83', '101', '82', '100',
257 '137', '248', '136', '247', '135', '246',
258 '245', '244', '134', '243', '133', '242', '160',
260 '141', '172', '140', '171', '139',
263 for f1id, f2id in zip(order, order[1:]):
264 f1 = YoutubeIE._formats[f1id].copy()
265 f1['format_id'] = f1id
266 f1['url'] = 'url:' + f1id
267 f2 = YoutubeIE._formats[f2id].copy()
268 f2['format_id'] = f2id
269 f2['url'] = 'url:' + f2id
271 info_dict = _make_result([f1, f2], extractor='youtube')
274 yie._sort_formats(info_dict['formats'])
275 ydl.process_ie_result(info_dict)
276 downloaded = ydl.downloaded_info_dicts[0]
277 self.assertEqual(downloaded['format_id'], f1id)
279 info_dict = _make_result([f2, f1], extractor='youtube')
282 yie._sort_formats(info_dict['formats'])
283 ydl.process_ie_result(info_dict)
284 downloaded = ydl.downloaded_info_dicts[0]
285 self.assertEqual(downloaded['format_id'], f1id)
287 def test_format_filtering(self):
289 {'format_id': 'A', 'filesize': 500, 'width': 1000},
290 {'format_id': 'B', 'filesize': 1000, 'width': 500},
291 {'format_id': 'C', 'filesize': 1000, 'width': 400},
292 {'format_id': 'D', 'filesize': 2000, 'width': 600},
293 {'format_id': 'E', 'filesize': 3000},
295 {'format_id': 'G', 'filesize': 1000000},
298 f['url'] = 'http://_/'
300 info_dict = _make_result(formats)
302 ydl = YDL({'format': 'best[filesize<3000]'})
303 ydl.process_ie_result(info_dict)
304 downloaded = ydl.downloaded_info_dicts[0]
305 self.assertEqual(downloaded['format_id'], 'D')
307 ydl = YDL({'format': 'best[filesize<=3000]'})
308 ydl.process_ie_result(info_dict)
309 downloaded = ydl.downloaded_info_dicts[0]
310 self.assertEqual(downloaded['format_id'], 'E')
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'], 'F')
317 ydl = YDL({'format': 'best [filesize = 1000] [width>450]'})
318 ydl.process_ie_result(info_dict)
319 downloaded = ydl.downloaded_info_dicts[0]
320 self.assertEqual(downloaded['format_id'], 'B')
322 ydl = YDL({'format': 'best [filesize = 1000] [width!=450]'})
323 ydl.process_ie_result(info_dict)
324 downloaded = ydl.downloaded_info_dicts[0]
325 self.assertEqual(downloaded['format_id'], 'C')
327 ydl = YDL({'format': '[filesize>?1]'})
328 ydl.process_ie_result(info_dict)
329 downloaded = ydl.downloaded_info_dicts[0]
330 self.assertEqual(downloaded['format_id'], 'G')
332 ydl = YDL({'format': '[filesize<1M]'})
333 ydl.process_ie_result(info_dict)
334 downloaded = ydl.downloaded_info_dicts[0]
335 self.assertEqual(downloaded['format_id'], 'E')
337 ydl = YDL({'format': '[filesize<1MiB]'})
338 ydl.process_ie_result(info_dict)
339 downloaded = ydl.downloaded_info_dicts[0]
340 self.assertEqual(downloaded['format_id'], 'G')
343 class TestYoutubeDL(unittest.TestCase):
344 def test_subtitles(self):
345 def s_formats(lang, autocaption=False):
348 'url': 'http://localhost/video.%s.%s' % (lang, ext),
349 '_auto': autocaption,
350 } for ext in ['vtt', 'srt', 'ass']]
351 subtitles = dict((l, s_formats(l)) for l in ['en', 'fr', 'es'])
352 auto_captions = dict((l, s_formats(l, True)) for l in ['it', 'pt', 'es'])
356 'url': 'http://localhost/video.mp4',
357 'subtitles': subtitles,
358 'automatic_captions': auto_captions,
362 def get_info(params={}):
363 params.setdefault('simulate', True)
365 ydl.report_warning = lambda *args, **kargs: None
366 return ydl.process_video_result(info_dict, download=False)
369 self.assertFalse(result.get('requested_subtitles'))
370 self.assertEqual(result['subtitles'], subtitles)
371 self.assertEqual(result['automatic_captions'], auto_captions)
373 result = get_info({'writesubtitles': True})
374 subs = result['requested_subtitles']
375 self.assertTrue(subs)
376 self.assertEqual(set(subs.keys()), set(['en']))
377 self.assertTrue(subs['en'].get('data') is None)
378 self.assertEqual(subs['en']['ext'], 'ass')
380 result = get_info({'writesubtitles': True, 'subtitlesformat': 'foo/srt'})
381 subs = result['requested_subtitles']
382 self.assertEqual(subs['en']['ext'], 'srt')
384 result = get_info({'writesubtitles': True, 'subtitleslangs': ['es', 'fr', 'it']})
385 subs = result['requested_subtitles']
386 self.assertTrue(subs)
387 self.assertEqual(set(subs.keys()), set(['es', 'fr']))
389 result = get_info({'writesubtitles': True, 'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
390 subs = result['requested_subtitles']
391 self.assertTrue(subs)
392 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
393 self.assertFalse(subs['es']['_auto'])
394 self.assertTrue(subs['pt']['_auto'])
396 result = get_info({'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
397 subs = result['requested_subtitles']
398 self.assertTrue(subs)
399 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
400 self.assertTrue(subs['es']['_auto'])
401 self.assertTrue(subs['pt']['_auto'])
403 def test_add_extra_info(self):
409 'playlist': 'funny videos',
411 YDL.add_extra_info(test_dict, extra_info)
412 self.assertEqual(test_dict['extractor'], 'Foo')
413 self.assertEqual(test_dict['playlist'], 'funny videos')
415 def test_prepare_filename(self):
423 ydl = YoutubeDL({'outtmpl': templ})
424 return ydl.prepare_filename(info)
425 self.assertEqual(fname('%(id)s.%(ext)s'), '1234.mp4')
426 self.assertEqual(fname('%(id)s-%(width)s.%(ext)s'), '1234-NA.mp4')
427 # Replace missing fields with 'NA'
428 self.assertEqual(fname('%(uploader_date)s-%(id)s.%(ext)s'), 'NA-1234.mp4')
430 def test_format_note(self):
432 self.assertEqual(ydl._format_note({}), '')
433 assertRegexpMatches(self, ydl._format_note({
437 def test_postprocessors(self):
438 filename = 'post-processor-testfile.mp4'
439 audiofile = filename + '.mp3'
441 class SimplePP(PostProcessor):
443 with open(audiofile, 'wt') as f:
449 with open(filename, 'wt') as f:
451 ydl = YoutubeDL(params)
452 ydl.add_post_processor(SimplePP())
453 ydl.post_process(filename, {'filepath': filename})
455 run_pp({'keepvideo': True})
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})
462 self.assertFalse(os.path.exists(filename), '%s exists' % filename)
463 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
467 if __name__ == '__main__':