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, compat_urllib_error
16 from youtube_dl.extractor import YoutubeIE
17 from youtube_dl.extractor.common import InfoExtractor
18 from youtube_dl.postprocessor.common import PostProcessor
19 from youtube_dl.utils import ExtractorError, match_filter_func
21 TEST_URL = 'http://localhost/sample.mp4'
25 def __init__(self, *args, **kwargs):
26 super(YDL, self).__init__(*args, **kwargs)
27 self.downloaded_info_dicts = []
30 def process_info(self, info_dict):
31 self.downloaded_info_dicts.append(info_dict)
33 def to_screen(self, msg):
37 def _make_result(formats, **kwargs):
41 'title': 'testttitle',
42 'extractor': 'testex',
48 class TestFormatSelection(unittest.TestCase):
49 def test_prefer_free_formats(self):
50 # Same resolution => download webm
52 ydl.params['prefer_free_formats'] = True
54 {'ext': 'webm', 'height': 460, 'url': TEST_URL},
55 {'ext': 'mp4', 'height': 460, 'url': TEST_URL},
57 info_dict = _make_result(formats)
59 yie._sort_formats(info_dict['formats'])
60 ydl.process_ie_result(info_dict)
61 downloaded = ydl.downloaded_info_dicts[0]
62 self.assertEqual(downloaded['ext'], 'webm')
64 # Different resolution => download best quality (mp4)
66 ydl.params['prefer_free_formats'] = True
68 {'ext': 'webm', 'height': 720, 'url': TEST_URL},
69 {'ext': 'mp4', 'height': 1080, 'url': TEST_URL},
71 info_dict['formats'] = formats
73 yie._sort_formats(info_dict['formats'])
74 ydl.process_ie_result(info_dict)
75 downloaded = ydl.downloaded_info_dicts[0]
76 self.assertEqual(downloaded['ext'], 'mp4')
78 # No prefer_free_formats => prefer mp4 and flv for greater compatibility
80 ydl.params['prefer_free_formats'] = False
82 {'ext': 'webm', 'height': 720, 'url': TEST_URL},
83 {'ext': 'mp4', 'height': 720, 'url': TEST_URL},
84 {'ext': 'flv', 'height': 720, 'url': TEST_URL},
86 info_dict['formats'] = formats
88 yie._sort_formats(info_dict['formats'])
89 ydl.process_ie_result(info_dict)
90 downloaded = ydl.downloaded_info_dicts[0]
91 self.assertEqual(downloaded['ext'], 'mp4')
94 ydl.params['prefer_free_formats'] = False
96 {'ext': 'flv', 'height': 720, 'url': TEST_URL},
97 {'ext': 'webm', 'height': 720, 'url': TEST_URL},
99 info_dict['formats'] = formats
101 yie._sort_formats(info_dict['formats'])
102 ydl.process_ie_result(info_dict)
103 downloaded = ydl.downloaded_info_dicts[0]
104 self.assertEqual(downloaded['ext'], 'flv')
106 def test_format_selection(self):
108 {'format_id': '35', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL},
109 {'format_id': 'example-with-dashes', 'ext': 'webm', 'preference': 1, 'url': TEST_URL},
110 {'format_id': '45', 'ext': 'webm', 'preference': 2, 'url': TEST_URL},
111 {'format_id': '47', 'ext': 'webm', 'preference': 3, 'url': TEST_URL},
112 {'format_id': '2', 'ext': 'flv', 'preference': 4, 'url': TEST_URL},
114 info_dict = _make_result(formats)
116 ydl = YDL({'format': '20/47'})
117 ydl.process_ie_result(info_dict.copy())
118 downloaded = ydl.downloaded_info_dicts[0]
119 self.assertEqual(downloaded['format_id'], '47')
121 ydl = YDL({'format': '20/71/worst'})
122 ydl.process_ie_result(info_dict.copy())
123 downloaded = ydl.downloaded_info_dicts[0]
124 self.assertEqual(downloaded['format_id'], '35')
127 ydl.process_ie_result(info_dict.copy())
128 downloaded = ydl.downloaded_info_dicts[0]
129 self.assertEqual(downloaded['format_id'], '2')
131 ydl = YDL({'format': 'webm/mp4'})
132 ydl.process_ie_result(info_dict.copy())
133 downloaded = ydl.downloaded_info_dicts[0]
134 self.assertEqual(downloaded['format_id'], '47')
136 ydl = YDL({'format': '3gp/40/mp4'})
137 ydl.process_ie_result(info_dict.copy())
138 downloaded = ydl.downloaded_info_dicts[0]
139 self.assertEqual(downloaded['format_id'], '35')
141 ydl = YDL({'format': 'example-with-dashes'})
142 ydl.process_ie_result(info_dict.copy())
143 downloaded = ydl.downloaded_info_dicts[0]
144 self.assertEqual(downloaded['format_id'], 'example-with-dashes')
146 def test_format_selection_audio(self):
148 {'format_id': 'audio-low', 'ext': 'webm', 'preference': 1, 'vcodec': 'none', 'url': TEST_URL},
149 {'format_id': 'audio-mid', 'ext': 'webm', 'preference': 2, 'vcodec': 'none', 'url': TEST_URL},
150 {'format_id': 'audio-high', 'ext': 'flv', 'preference': 3, 'vcodec': 'none', 'url': TEST_URL},
151 {'format_id': 'vid', 'ext': 'mp4', 'preference': 4, 'url': TEST_URL},
153 info_dict = _make_result(formats)
155 ydl = YDL({'format': 'bestaudio'})
156 ydl.process_ie_result(info_dict.copy())
157 downloaded = ydl.downloaded_info_dicts[0]
158 self.assertEqual(downloaded['format_id'], 'audio-high')
160 ydl = YDL({'format': 'worstaudio'})
161 ydl.process_ie_result(info_dict.copy())
162 downloaded = ydl.downloaded_info_dicts[0]
163 self.assertEqual(downloaded['format_id'], 'audio-low')
166 {'format_id': 'vid-low', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL},
167 {'format_id': 'vid-high', 'ext': 'mp4', 'preference': 2, 'url': TEST_URL},
169 info_dict = _make_result(formats)
171 ydl = YDL({'format': 'bestaudio/worstaudio/best'})
172 ydl.process_ie_result(info_dict.copy())
173 downloaded = ydl.downloaded_info_dicts[0]
174 self.assertEqual(downloaded['format_id'], 'vid-high')
176 def test_format_selection_audio_exts(self):
178 {'format_id': 'mp3-64', 'ext': 'mp3', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
179 {'format_id': 'ogg-64', 'ext': 'ogg', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
180 {'format_id': 'aac-64', 'ext': 'aac', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
181 {'format_id': 'mp3-32', 'ext': 'mp3', 'abr': 32, 'url': 'http://_', 'vcodec': 'none'},
182 {'format_id': 'aac-32', 'ext': 'aac', 'abr': 32, 'url': 'http://_', 'vcodec': 'none'},
185 info_dict = _make_result(formats)
186 ydl = YDL({'format': 'best'})
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'], 'aac-64')
193 ydl = YDL({'format': 'mp3'})
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'], 'mp3-64')
200 ydl = YDL({'prefer_free_formats': True})
202 ie._sort_formats(info_dict['formats'])
203 ydl.process_ie_result(copy.deepcopy(info_dict))
204 downloaded = ydl.downloaded_info_dicts[0]
205 self.assertEqual(downloaded['format_id'], 'ogg-64')
207 def test_format_selection_video(self):
209 {'format_id': 'dash-video-low', 'ext': 'mp4', 'preference': 1, 'acodec': 'none', 'url': TEST_URL},
210 {'format_id': 'dash-video-high', 'ext': 'mp4', 'preference': 2, 'acodec': 'none', 'url': TEST_URL},
211 {'format_id': 'vid', 'ext': 'mp4', 'preference': 3, 'url': TEST_URL},
213 info_dict = _make_result(formats)
215 ydl = YDL({'format': 'bestvideo'})
216 ydl.process_ie_result(info_dict.copy())
217 downloaded = ydl.downloaded_info_dicts[0]
218 self.assertEqual(downloaded['format_id'], 'dash-video-high')
220 ydl = YDL({'format': 'worstvideo'})
221 ydl.process_ie_result(info_dict.copy())
222 downloaded = ydl.downloaded_info_dicts[0]
223 self.assertEqual(downloaded['format_id'], 'dash-video-low')
226 {'format_id': 'vid-vcodec-dot', 'ext': 'mp4', 'preference': 1, 'vcodec': 'avc1.123456', 'acodec': 'none', 'url': TEST_URL},
228 info_dict = _make_result(formats)
230 ydl = YDL({'format': 'bestvideo[vcodec=avc1.123456]'})
231 ydl.process_ie_result(info_dict.copy())
232 downloaded = ydl.downloaded_info_dicts[0]
233 self.assertEqual(downloaded['format_id'], 'vid-vcodec-dot')
235 def test_youtube_format_selection(self):
237 '38', '37', '46', '22', '45', '35', '44', '18', '34', '43', '6', '5', '36', '17', '13',
238 # Apple HTTP Live Streaming
239 '96', '95', '94', '93', '92', '132', '151',
241 '85', '84', '102', '83', '101', '82', '100',
243 '137', '248', '136', '247', '135', '246',
244 '245', '244', '134', '243', '133', '242', '160',
246 '141', '172', '140', '171', '139',
249 def format_info(f_id):
250 info = YoutubeIE._formats[f_id].copy()
251 info['format_id'] = f_id
252 info['url'] = 'url:' + f_id
254 formats_order = [format_info(f_id) for f_id in order]
256 info_dict = _make_result(list(formats_order), extractor='youtube')
257 ydl = YDL({'format': 'bestvideo+bestaudio'})
259 yie._sort_formats(info_dict['formats'])
260 ydl.process_ie_result(info_dict)
261 downloaded = ydl.downloaded_info_dicts[0]
262 self.assertEqual(downloaded['format_id'], '137+141')
263 self.assertEqual(downloaded['ext'], 'mp4')
265 info_dict = _make_result(list(formats_order), extractor='youtube')
266 ydl = YDL({'format': 'bestvideo[height>=999999]+bestaudio/best'})
268 yie._sort_formats(info_dict['formats'])
269 ydl.process_ie_result(info_dict)
270 downloaded = ydl.downloaded_info_dicts[0]
271 self.assertEqual(downloaded['format_id'], '38')
273 info_dict = _make_result(list(formats_order), extractor='youtube')
274 ydl = YDL({'format': 'bestvideo/best,bestaudio'})
276 yie._sort_formats(info_dict['formats'])
277 ydl.process_ie_result(info_dict)
278 downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
279 self.assertEqual(downloaded_ids, ['137', '141'])
281 info_dict = _make_result(list(formats_order), extractor='youtube')
282 ydl = YDL({'format': '(bestvideo[ext=mp4],bestvideo[ext=webm])+bestaudio'})
284 yie._sort_formats(info_dict['formats'])
285 ydl.process_ie_result(info_dict)
286 downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
287 self.assertEqual(downloaded_ids, ['137+141', '248+141'])
289 info_dict = _make_result(list(formats_order), extractor='youtube')
290 ydl = YDL({'format': '(bestvideo[ext=mp4],bestvideo[ext=webm])[height<=720]+bestaudio'})
292 yie._sort_formats(info_dict['formats'])
293 ydl.process_ie_result(info_dict)
294 downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
295 self.assertEqual(downloaded_ids, ['136+141', '247+141'])
297 info_dict = _make_result(list(formats_order), extractor='youtube')
298 ydl = YDL({'format': '(bestvideo[ext=none]/bestvideo[ext=webm])+bestaudio'})
300 yie._sort_formats(info_dict['formats'])
301 ydl.process_ie_result(info_dict)
302 downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
303 self.assertEqual(downloaded_ids, ['248+141'])
305 for f1, f2 in zip(formats_order, formats_order[1:]):
306 info_dict = _make_result([f1, f2], extractor='youtube')
307 ydl = YDL({'format': 'best/bestvideo'})
309 yie._sort_formats(info_dict['formats'])
310 ydl.process_ie_result(info_dict)
311 downloaded = ydl.downloaded_info_dicts[0]
312 self.assertEqual(downloaded['format_id'], f1['format_id'])
314 info_dict = _make_result([f2, f1], extractor='youtube')
315 ydl = YDL({'format': 'best/bestvideo'})
317 yie._sort_formats(info_dict['formats'])
318 ydl.process_ie_result(info_dict)
319 downloaded = ydl.downloaded_info_dicts[0]
320 self.assertEqual(downloaded['format_id'], f1['format_id'])
322 def test_invalid_format_specs(self):
323 def assert_syntax_error(format_spec):
324 ydl = YDL({'format': format_spec})
325 info_dict = _make_result([{'format_id': 'foo', 'url': TEST_URL}])
326 self.assertRaises(SyntaxError, ydl.process_ie_result, info_dict)
328 assert_syntax_error('bestvideo,,best')
329 assert_syntax_error('+bestaudio')
330 assert_syntax_error('bestvideo+')
331 assert_syntax_error('/')
333 def test_format_filtering(self):
335 {'format_id': 'A', 'filesize': 500, 'width': 1000},
336 {'format_id': 'B', 'filesize': 1000, 'width': 500},
337 {'format_id': 'C', 'filesize': 1000, 'width': 400},
338 {'format_id': 'D', 'filesize': 2000, 'width': 600},
339 {'format_id': 'E', 'filesize': 3000},
341 {'format_id': 'G', 'filesize': 1000000},
344 f['url'] = 'http://_/'
346 info_dict = _make_result(formats)
348 ydl = YDL({'format': 'best[filesize<3000]'})
349 ydl.process_ie_result(info_dict)
350 downloaded = ydl.downloaded_info_dicts[0]
351 self.assertEqual(downloaded['format_id'], 'D')
353 ydl = YDL({'format': 'best[filesize<=3000]'})
354 ydl.process_ie_result(info_dict)
355 downloaded = ydl.downloaded_info_dicts[0]
356 self.assertEqual(downloaded['format_id'], 'E')
358 ydl = YDL({'format': 'best[filesize <= ? 3000]'})
359 ydl.process_ie_result(info_dict)
360 downloaded = ydl.downloaded_info_dicts[0]
361 self.assertEqual(downloaded['format_id'], 'F')
363 ydl = YDL({'format': 'best [filesize = 1000] [width>450]'})
364 ydl.process_ie_result(info_dict)
365 downloaded = ydl.downloaded_info_dicts[0]
366 self.assertEqual(downloaded['format_id'], 'B')
368 ydl = YDL({'format': 'best [filesize = 1000] [width!=450]'})
369 ydl.process_ie_result(info_dict)
370 downloaded = ydl.downloaded_info_dicts[0]
371 self.assertEqual(downloaded['format_id'], 'C')
373 ydl = YDL({'format': '[filesize>?1]'})
374 ydl.process_ie_result(info_dict)
375 downloaded = ydl.downloaded_info_dicts[0]
376 self.assertEqual(downloaded['format_id'], 'G')
378 ydl = YDL({'format': '[filesize<1M]'})
379 ydl.process_ie_result(info_dict)
380 downloaded = ydl.downloaded_info_dicts[0]
381 self.assertEqual(downloaded['format_id'], 'E')
383 ydl = YDL({'format': '[filesize<1MiB]'})
384 ydl.process_ie_result(info_dict)
385 downloaded = ydl.downloaded_info_dicts[0]
386 self.assertEqual(downloaded['format_id'], 'G')
388 ydl = YDL({'format': 'all[width>=400][width<=600]'})
389 ydl.process_ie_result(info_dict)
390 downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
391 self.assertEqual(downloaded_ids, ['B', 'C', 'D'])
393 ydl = YDL({'format': 'best[height<40]'})
395 ydl.process_ie_result(info_dict)
396 except ExtractorError:
398 self.assertEqual(ydl.downloaded_info_dicts, [])
401 class TestYoutubeDL(unittest.TestCase):
402 def test_subtitles(self):
403 def s_formats(lang, autocaption=False):
406 'url': 'http://localhost/video.%s.%s' % (lang, ext),
407 '_auto': autocaption,
408 } for ext in ['vtt', 'srt', 'ass']]
409 subtitles = dict((l, s_formats(l)) for l in ['en', 'fr', 'es'])
410 auto_captions = dict((l, s_formats(l, True)) for l in ['it', 'pt', 'es'])
414 'url': 'http://localhost/video.mp4',
415 'subtitles': subtitles,
416 'automatic_captions': auto_captions,
420 def get_info(params={}):
421 params.setdefault('simulate', True)
423 ydl.report_warning = lambda *args, **kargs: None
424 return ydl.process_video_result(info_dict, download=False)
427 self.assertFalse(result.get('requested_subtitles'))
428 self.assertEqual(result['subtitles'], subtitles)
429 self.assertEqual(result['automatic_captions'], auto_captions)
431 result = get_info({'writesubtitles': True})
432 subs = result['requested_subtitles']
433 self.assertTrue(subs)
434 self.assertEqual(set(subs.keys()), set(['en']))
435 self.assertTrue(subs['en'].get('data') is None)
436 self.assertEqual(subs['en']['ext'], 'ass')
438 result = get_info({'writesubtitles': True, 'subtitlesformat': 'foo/srt'})
439 subs = result['requested_subtitles']
440 self.assertEqual(subs['en']['ext'], 'srt')
442 result = get_info({'writesubtitles': True, 'subtitleslangs': ['es', 'fr', 'it']})
443 subs = result['requested_subtitles']
444 self.assertTrue(subs)
445 self.assertEqual(set(subs.keys()), set(['es', 'fr']))
447 result = get_info({'writesubtitles': True, 'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
448 subs = result['requested_subtitles']
449 self.assertTrue(subs)
450 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
451 self.assertFalse(subs['es']['_auto'])
452 self.assertTrue(subs['pt']['_auto'])
454 result = get_info({'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
455 subs = result['requested_subtitles']
456 self.assertTrue(subs)
457 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
458 self.assertTrue(subs['es']['_auto'])
459 self.assertTrue(subs['pt']['_auto'])
461 def test_add_extra_info(self):
467 'playlist': 'funny videos',
469 YDL.add_extra_info(test_dict, extra_info)
470 self.assertEqual(test_dict['extractor'], 'Foo')
471 self.assertEqual(test_dict['playlist'], 'funny videos')
473 def test_prepare_filename(self):
481 ydl = YoutubeDL({'outtmpl': templ})
482 return ydl.prepare_filename(info)
483 self.assertEqual(fname('%(id)s.%(ext)s'), '1234.mp4')
484 self.assertEqual(fname('%(id)s-%(width)s.%(ext)s'), '1234-NA.mp4')
485 # Replace missing fields with 'NA'
486 self.assertEqual(fname('%(uploader_date)s-%(id)s.%(ext)s'), 'NA-1234.mp4')
488 def test_format_note(self):
490 self.assertEqual(ydl._format_note({}), '')
491 assertRegexpMatches(self, ydl._format_note({
495 def test_postprocessors(self):
496 filename = 'post-processor-testfile.mp4'
497 audiofile = filename + '.mp3'
499 class SimplePP(PostProcessor):
501 with open(audiofile, 'wt') as f:
503 return [info['filepath']], info
505 def run_pp(params, PP):
506 with open(filename, 'wt') as f:
508 ydl = YoutubeDL(params)
509 ydl.add_post_processor(PP())
510 ydl.post_process(filename, {'filepath': filename})
512 run_pp({'keepvideo': True}, SimplePP)
513 self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
514 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
518 run_pp({'keepvideo': False}, SimplePP)
519 self.assertFalse(os.path.exists(filename), '%s exists' % filename)
520 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
523 class ModifierPP(PostProcessor):
525 with open(info['filepath'], 'wt') as f:
529 run_pp({'keepvideo': False}, ModifierPP)
530 self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
533 def test_match_filter(self):
534 class FilterYDL(YDL):
535 def __init__(self, *args, **kwargs):
536 super(FilterYDL, self).__init__(*args, **kwargs)
537 self.params['simulate'] = True
539 def process_info(self, info_dict):
540 super(YDL, self).process_info(info_dict)
542 def _match_entry(self, info_dict, incomplete):
543 res = super(FilterYDL, self)._match_entry(info_dict, incomplete)
545 self.downloaded_info_dicts.append(info_dict)
554 'filesize': 10 * 1024,
562 'description': 'foo',
563 'filesize': 5 * 1024,
565 videos = [first, second]
567 def get_videos(filter_=None):
568 ydl = FilterYDL({'match_filter': filter_})
570 ydl.process_ie_result(v, download=True)
571 return [v['id'] for v in ydl.downloaded_info_dicts]
574 self.assertEqual(res, ['1', '2'])
580 return 'Video id is not 1'
582 self.assertEqual(res, ['1'])
584 f = match_filter_func('duration < 30')
586 self.assertEqual(res, ['2'])
588 f = match_filter_func('description = foo')
590 self.assertEqual(res, ['2'])
592 f = match_filter_func('description =? foo')
594 self.assertEqual(res, ['1', '2'])
596 f = match_filter_func('filesize > 5KiB')
598 self.assertEqual(res, ['1'])
600 def test_playlist_items_selection(self):
603 'title': compat_str(i),
605 } for i in range(1, 5)]
610 'extractor': 'test:playlist',
611 'extractor_key': 'test:playlist',
612 'webpage_url': 'http://example.com',
617 # make a copy because the dictionary can be modified
618 ydl.process_ie_result(playlist.copy())
619 return [int(v['id']) for v in ydl.downloaded_info_dicts]
622 self.assertEqual(result, [1, 2, 3, 4])
624 result = get_ids({'playlistend': 10})
625 self.assertEqual(result, [1, 2, 3, 4])
627 result = get_ids({'playlistend': 2})
628 self.assertEqual(result, [1, 2])
630 result = get_ids({'playliststart': 10})
631 self.assertEqual(result, [])
633 result = get_ids({'playliststart': 2})
634 self.assertEqual(result, [2, 3, 4])
636 result = get_ids({'playlist_items': '2-4'})
637 self.assertEqual(result, [2, 3, 4])
639 result = get_ids({'playlist_items': '2,4'})
640 self.assertEqual(result, [2, 4])
642 result = get_ids({'playlist_items': '10'})
643 self.assertEqual(result, [])
645 def test_urlopen_no_file_protocol(self):
646 # see https://github.com/rg3/youtube-dl/issues/8227
648 self.assertRaises(compat_urllib_error.URLError, ydl.urlopen, 'file:///etc/passwd')
650 def test_do_not_override_ie_key_in_url_transparent(self):
653 class Foo1IE(InfoExtractor):
654 _VALID_URL = r'foo1:'
656 def _real_extract(self, url):
658 '_type': 'url_transparent',
663 class Foo2IE(InfoExtractor):
664 _VALID_URL = r'foo2:'
666 def _real_extract(self, url):
673 class Foo3IE(InfoExtractor):
674 _VALID_URL = r'foo3:'
676 def _real_extract(self, url):
677 return _make_result([{'url': TEST_URL}])
679 ydl.add_info_extractor(Foo1IE(ydl))
680 ydl.add_info_extractor(Foo2IE(ydl))
681 ydl.add_info_extractor(Foo3IE(ydl))
682 ydl.extract_info('foo1:')
683 downloaded = ydl.downloaded_info_dicts[0]
684 self.assertEqual(downloaded['url'], TEST_URL)
687 if __name__ == '__main__':