4 from __future__ import unicode_literals
6 # Allow direct execution
10 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
14 from test.helper import FakeYDL, assertRegexpMatches
15 from youtube_dl import YoutubeDL
16 from youtube_dl.compat import compat_str, compat_urllib_error
17 from youtube_dl.extractor import YoutubeIE
18 from youtube_dl.extractor.common import InfoExtractor
19 from youtube_dl.postprocessor.common import PostProcessor
20 from youtube_dl.utils import ExtractorError, match_filter_func
22 TEST_URL = 'http://localhost/sample.mp4'
26 def __init__(self, *args, **kwargs):
27 super(YDL, self).__init__(*args, **kwargs)
28 self.downloaded_info_dicts = []
31 def process_info(self, info_dict):
32 self.downloaded_info_dicts.append(info_dict)
34 def to_screen(self, msg):
38 def _make_result(formats, **kwargs):
42 'title': 'testttitle',
43 'extractor': 'testex',
44 'extractor_key': 'TestEx',
50 class TestFormatSelection(unittest.TestCase):
51 def test_prefer_free_formats(self):
52 # Same resolution => download webm
54 ydl.params['prefer_free_formats'] = True
56 {'ext': 'webm', 'height': 460, 'url': TEST_URL},
57 {'ext': 'mp4', 'height': 460, 'url': TEST_URL},
59 info_dict = _make_result(formats)
61 yie._sort_formats(info_dict['formats'])
62 ydl.process_ie_result(info_dict)
63 downloaded = ydl.downloaded_info_dicts[0]
64 self.assertEqual(downloaded['ext'], 'webm')
66 # Different resolution => download best quality (mp4)
68 ydl.params['prefer_free_formats'] = True
70 {'ext': 'webm', 'height': 720, 'url': TEST_URL},
71 {'ext': 'mp4', 'height': 1080, 'url': TEST_URL},
73 info_dict['formats'] = formats
75 yie._sort_formats(info_dict['formats'])
76 ydl.process_ie_result(info_dict)
77 downloaded = ydl.downloaded_info_dicts[0]
78 self.assertEqual(downloaded['ext'], 'mp4')
80 # No prefer_free_formats => prefer mp4 and flv for greater compatibility
82 ydl.params['prefer_free_formats'] = False
84 {'ext': 'webm', 'height': 720, 'url': TEST_URL},
85 {'ext': 'mp4', 'height': 720, 'url': TEST_URL},
86 {'ext': 'flv', 'height': 720, 'url': TEST_URL},
88 info_dict['formats'] = formats
90 yie._sort_formats(info_dict['formats'])
91 ydl.process_ie_result(info_dict)
92 downloaded = ydl.downloaded_info_dicts[0]
93 self.assertEqual(downloaded['ext'], 'mp4')
96 ydl.params['prefer_free_formats'] = False
98 {'ext': 'flv', 'height': 720, 'url': TEST_URL},
99 {'ext': 'webm', 'height': 720, 'url': TEST_URL},
101 info_dict['formats'] = formats
103 yie._sort_formats(info_dict['formats'])
104 ydl.process_ie_result(info_dict)
105 downloaded = ydl.downloaded_info_dicts[0]
106 self.assertEqual(downloaded['ext'], 'flv')
108 def test_format_selection(self):
110 {'format_id': '35', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL},
111 {'format_id': 'example-with-dashes', 'ext': 'webm', 'preference': 1, 'url': TEST_URL},
112 {'format_id': '45', 'ext': 'webm', 'preference': 2, 'url': TEST_URL},
113 {'format_id': '47', 'ext': 'webm', 'preference': 3, 'url': TEST_URL},
114 {'format_id': '2', 'ext': 'flv', 'preference': 4, 'url': TEST_URL},
116 info_dict = _make_result(formats)
118 ydl = YDL({'format': '20/47'})
119 ydl.process_ie_result(info_dict.copy())
120 downloaded = ydl.downloaded_info_dicts[0]
121 self.assertEqual(downloaded['format_id'], '47')
123 ydl = YDL({'format': '20/71/worst'})
124 ydl.process_ie_result(info_dict.copy())
125 downloaded = ydl.downloaded_info_dicts[0]
126 self.assertEqual(downloaded['format_id'], '35')
129 ydl.process_ie_result(info_dict.copy())
130 downloaded = ydl.downloaded_info_dicts[0]
131 self.assertEqual(downloaded['format_id'], '2')
133 ydl = YDL({'format': 'webm/mp4'})
134 ydl.process_ie_result(info_dict.copy())
135 downloaded = ydl.downloaded_info_dicts[0]
136 self.assertEqual(downloaded['format_id'], '47')
138 ydl = YDL({'format': '3gp/40/mp4'})
139 ydl.process_ie_result(info_dict.copy())
140 downloaded = ydl.downloaded_info_dicts[0]
141 self.assertEqual(downloaded['format_id'], '35')
143 ydl = YDL({'format': 'example-with-dashes'})
144 ydl.process_ie_result(info_dict.copy())
145 downloaded = ydl.downloaded_info_dicts[0]
146 self.assertEqual(downloaded['format_id'], 'example-with-dashes')
148 def test_format_selection_audio(self):
150 {'format_id': 'audio-low', 'ext': 'webm', 'preference': 1, 'vcodec': 'none', 'url': TEST_URL},
151 {'format_id': 'audio-mid', 'ext': 'webm', 'preference': 2, 'vcodec': 'none', 'url': TEST_URL},
152 {'format_id': 'audio-high', 'ext': 'flv', 'preference': 3, 'vcodec': 'none', 'url': TEST_URL},
153 {'format_id': 'vid', 'ext': 'mp4', 'preference': 4, 'url': TEST_URL},
155 info_dict = _make_result(formats)
157 ydl = YDL({'format': 'bestaudio'})
158 ydl.process_ie_result(info_dict.copy())
159 downloaded = ydl.downloaded_info_dicts[0]
160 self.assertEqual(downloaded['format_id'], 'audio-high')
162 ydl = YDL({'format': 'worstaudio'})
163 ydl.process_ie_result(info_dict.copy())
164 downloaded = ydl.downloaded_info_dicts[0]
165 self.assertEqual(downloaded['format_id'], 'audio-low')
168 {'format_id': 'vid-low', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL},
169 {'format_id': 'vid-high', 'ext': 'mp4', 'preference': 2, 'url': TEST_URL},
171 info_dict = _make_result(formats)
173 ydl = YDL({'format': 'bestaudio/worstaudio/best'})
174 ydl.process_ie_result(info_dict.copy())
175 downloaded = ydl.downloaded_info_dicts[0]
176 self.assertEqual(downloaded['format_id'], 'vid-high')
178 def test_format_selection_audio_exts(self):
180 {'format_id': 'mp3-64', 'ext': 'mp3', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
181 {'format_id': 'ogg-64', 'ext': 'ogg', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
182 {'format_id': 'aac-64', 'ext': 'aac', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
183 {'format_id': 'mp3-32', 'ext': 'mp3', 'abr': 32, 'url': 'http://_', 'vcodec': 'none'},
184 {'format_id': 'aac-32', 'ext': 'aac', 'abr': 32, 'url': 'http://_', 'vcodec': 'none'},
187 info_dict = _make_result(formats)
188 ydl = YDL({'format': 'best'})
190 ie._sort_formats(info_dict['formats'])
191 ydl.process_ie_result(copy.deepcopy(info_dict))
192 downloaded = ydl.downloaded_info_dicts[0]
193 self.assertEqual(downloaded['format_id'], 'aac-64')
195 ydl = YDL({'format': 'mp3'})
197 ie._sort_formats(info_dict['formats'])
198 ydl.process_ie_result(copy.deepcopy(info_dict))
199 downloaded = ydl.downloaded_info_dicts[0]
200 self.assertEqual(downloaded['format_id'], 'mp3-64')
202 ydl = YDL({'prefer_free_formats': True})
204 ie._sort_formats(info_dict['formats'])
205 ydl.process_ie_result(copy.deepcopy(info_dict))
206 downloaded = ydl.downloaded_info_dicts[0]
207 self.assertEqual(downloaded['format_id'], 'ogg-64')
209 def test_format_selection_video(self):
211 {'format_id': 'dash-video-low', 'ext': 'mp4', 'preference': 1, 'acodec': 'none', 'url': TEST_URL},
212 {'format_id': 'dash-video-high', 'ext': 'mp4', 'preference': 2, 'acodec': 'none', 'url': TEST_URL},
213 {'format_id': 'vid', 'ext': 'mp4', 'preference': 3, 'url': TEST_URL},
215 info_dict = _make_result(formats)
217 ydl = YDL({'format': 'bestvideo'})
218 ydl.process_ie_result(info_dict.copy())
219 downloaded = ydl.downloaded_info_dicts[0]
220 self.assertEqual(downloaded['format_id'], 'dash-video-high')
222 ydl = YDL({'format': 'worstvideo'})
223 ydl.process_ie_result(info_dict.copy())
224 downloaded = ydl.downloaded_info_dicts[0]
225 self.assertEqual(downloaded['format_id'], 'dash-video-low')
227 ydl = YDL({'format': 'bestvideo[format_id^=dash][format_id$=low]'})
228 ydl.process_ie_result(info_dict.copy())
229 downloaded = ydl.downloaded_info_dicts[0]
230 self.assertEqual(downloaded['format_id'], 'dash-video-low')
233 {'format_id': 'vid-vcodec-dot', 'ext': 'mp4', 'preference': 1, 'vcodec': 'avc1.123456', 'acodec': 'none', 'url': TEST_URL},
235 info_dict = _make_result(formats)
237 ydl = YDL({'format': 'bestvideo[vcodec=avc1.123456]'})
238 ydl.process_ie_result(info_dict.copy())
239 downloaded = ydl.downloaded_info_dicts[0]
240 self.assertEqual(downloaded['format_id'], 'vid-vcodec-dot')
242 def test_format_selection_string_ops(self):
244 {'format_id': 'abc-cba', 'ext': 'mp4', 'url': TEST_URL},
245 {'format_id': 'zxc-cxz', 'ext': 'webm', 'url': TEST_URL},
247 info_dict = _make_result(formats)
250 ydl = YDL({'format': '[format_id=abc-cba]'})
251 ydl.process_ie_result(info_dict.copy())
252 downloaded = ydl.downloaded_info_dicts[0]
253 self.assertEqual(downloaded['format_id'], 'abc-cba')
255 # does not equal (!=)
256 ydl = YDL({'format': '[format_id!=abc-cba]'})
257 ydl.process_ie_result(info_dict.copy())
258 downloaded = ydl.downloaded_info_dicts[0]
259 self.assertEqual(downloaded['format_id'], 'zxc-cxz')
261 ydl = YDL({'format': '[format_id!=abc-cba][format_id!=zxc-cxz]'})
262 self.assertRaises(ExtractorError, ydl.process_ie_result, info_dict.copy())
265 ydl = YDL({'format': '[format_id^=abc]'})
266 ydl.process_ie_result(info_dict.copy())
267 downloaded = ydl.downloaded_info_dicts[0]
268 self.assertEqual(downloaded['format_id'], 'abc-cba')
270 # does not start with (!^=)
271 ydl = YDL({'format': '[format_id!^=abc]'})
272 ydl.process_ie_result(info_dict.copy())
273 downloaded = ydl.downloaded_info_dicts[0]
274 self.assertEqual(downloaded['format_id'], 'zxc-cxz')
276 ydl = YDL({'format': '[format_id!^=abc][format_id!^=zxc]'})
277 self.assertRaises(ExtractorError, ydl.process_ie_result, info_dict.copy())
280 ydl = YDL({'format': '[format_id$=cba]'})
281 ydl.process_ie_result(info_dict.copy())
282 downloaded = ydl.downloaded_info_dicts[0]
283 self.assertEqual(downloaded['format_id'], 'abc-cba')
285 # does not end with (!$=)
286 ydl = YDL({'format': '[format_id!$=cba]'})
287 ydl.process_ie_result(info_dict.copy())
288 downloaded = ydl.downloaded_info_dicts[0]
289 self.assertEqual(downloaded['format_id'], 'zxc-cxz')
291 ydl = YDL({'format': '[format_id!$=cba][format_id!$=cxz]'})
292 self.assertRaises(ExtractorError, ydl.process_ie_result, info_dict.copy())
295 ydl = YDL({'format': '[format_id*=bc-cb]'})
296 ydl.process_ie_result(info_dict.copy())
297 downloaded = ydl.downloaded_info_dicts[0]
298 self.assertEqual(downloaded['format_id'], 'abc-cba')
300 # does not contain (!*=)
301 ydl = YDL({'format': '[format_id!*=bc-cb]'})
302 ydl.process_ie_result(info_dict.copy())
303 downloaded = ydl.downloaded_info_dicts[0]
304 self.assertEqual(downloaded['format_id'], 'zxc-cxz')
306 ydl = YDL({'format': '[format_id!*=abc][format_id!*=zxc]'})
307 self.assertRaises(ExtractorError, ydl.process_ie_result, info_dict.copy())
309 ydl = YDL({'format': '[format_id!*=-]'})
310 self.assertRaises(ExtractorError, ydl.process_ie_result, info_dict.copy())
312 def test_youtube_format_selection(self):
314 '38', '37', '46', '22', '45', '35', '44', '18', '34', '43', '6', '5', '17', '36', '13',
315 # Apple HTTP Live Streaming
316 '96', '95', '94', '93', '92', '132', '151',
318 '85', '84', '102', '83', '101', '82', '100',
320 '137', '248', '136', '247', '135', '246',
321 '245', '244', '134', '243', '133', '242', '160',
323 '141', '172', '140', '171', '139',
326 def format_info(f_id):
327 info = YoutubeIE._formats[f_id].copy()
329 # XXX: In real cases InfoExtractor._parse_mpd_formats() fills up 'acodec'
330 # and 'vcodec', while in tests such information is incomplete since
331 # commit a6c2c24479e5f4827ceb06f64d855329c0a6f593
332 # test_YoutubeDL.test_youtube_format_selection is broken without
334 if 'acodec' in info and 'vcodec' not in info:
335 info['vcodec'] = 'none'
336 elif 'vcodec' in info and 'acodec' not in info:
337 info['acodec'] = 'none'
339 info['format_id'] = f_id
340 info['url'] = 'url:' + f_id
342 formats_order = [format_info(f_id) for f_id in order]
344 info_dict = _make_result(list(formats_order), extractor='youtube')
345 ydl = YDL({'format': 'bestvideo+bestaudio'})
347 yie._sort_formats(info_dict['formats'])
348 ydl.process_ie_result(info_dict)
349 downloaded = ydl.downloaded_info_dicts[0]
350 self.assertEqual(downloaded['format_id'], '137+141')
351 self.assertEqual(downloaded['ext'], 'mp4')
353 info_dict = _make_result(list(formats_order), extractor='youtube')
354 ydl = YDL({'format': 'bestvideo[height>=999999]+bestaudio/best'})
356 yie._sort_formats(info_dict['formats'])
357 ydl.process_ie_result(info_dict)
358 downloaded = ydl.downloaded_info_dicts[0]
359 self.assertEqual(downloaded['format_id'], '38')
361 info_dict = _make_result(list(formats_order), extractor='youtube')
362 ydl = YDL({'format': 'bestvideo/best,bestaudio'})
364 yie._sort_formats(info_dict['formats'])
365 ydl.process_ie_result(info_dict)
366 downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
367 self.assertEqual(downloaded_ids, ['137', '141'])
369 info_dict = _make_result(list(formats_order), extractor='youtube')
370 ydl = YDL({'format': '(bestvideo[ext=mp4],bestvideo[ext=webm])+bestaudio'})
372 yie._sort_formats(info_dict['formats'])
373 ydl.process_ie_result(info_dict)
374 downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
375 self.assertEqual(downloaded_ids, ['137+141', '248+141'])
377 info_dict = _make_result(list(formats_order), extractor='youtube')
378 ydl = YDL({'format': '(bestvideo[ext=mp4],bestvideo[ext=webm])[height<=720]+bestaudio'})
380 yie._sort_formats(info_dict['formats'])
381 ydl.process_ie_result(info_dict)
382 downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
383 self.assertEqual(downloaded_ids, ['136+141', '247+141'])
385 info_dict = _make_result(list(formats_order), extractor='youtube')
386 ydl = YDL({'format': '(bestvideo[ext=none]/bestvideo[ext=webm])+bestaudio'})
388 yie._sort_formats(info_dict['formats'])
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, ['248+141'])
393 for f1, f2 in zip(formats_order, formats_order[1:]):
394 info_dict = _make_result([f1, f2], extractor='youtube')
395 ydl = YDL({'format': 'best/bestvideo'})
397 yie._sort_formats(info_dict['formats'])
398 ydl.process_ie_result(info_dict)
399 downloaded = ydl.downloaded_info_dicts[0]
400 self.assertEqual(downloaded['format_id'], f1['format_id'])
402 info_dict = _make_result([f2, f1], extractor='youtube')
403 ydl = YDL({'format': 'best/bestvideo'})
405 yie._sort_formats(info_dict['formats'])
406 ydl.process_ie_result(info_dict)
407 downloaded = ydl.downloaded_info_dicts[0]
408 self.assertEqual(downloaded['format_id'], f1['format_id'])
410 def test_audio_only_extractor_format_selection(self):
411 # For extractors with incomplete formats (all formats are audio-only or
412 # video-only) best and worst should fallback to corresponding best/worst
413 # video-only or audio-only formats (as per
414 # https://github.com/ytdl-org/youtube-dl/pull/5556)
416 {'format_id': 'low', 'ext': 'mp3', 'preference': 1, 'vcodec': 'none', 'url': TEST_URL},
417 {'format_id': 'high', 'ext': 'mp3', 'preference': 2, 'vcodec': 'none', 'url': TEST_URL},
419 info_dict = _make_result(formats)
421 ydl = YDL({'format': 'best'})
422 ydl.process_ie_result(info_dict.copy())
423 downloaded = ydl.downloaded_info_dicts[0]
424 self.assertEqual(downloaded['format_id'], 'high')
426 ydl = YDL({'format': 'worst'})
427 ydl.process_ie_result(info_dict.copy())
428 downloaded = ydl.downloaded_info_dicts[0]
429 self.assertEqual(downloaded['format_id'], 'low')
431 def test_format_not_available(self):
433 {'format_id': 'regular', 'ext': 'mp4', 'height': 360, 'url': TEST_URL},
434 {'format_id': 'video', 'ext': 'mp4', 'height': 720, 'acodec': 'none', 'url': TEST_URL},
436 info_dict = _make_result(formats)
438 # This must fail since complete video-audio format does not match filter
439 # and extractor does not provide incomplete only formats (i.e. only
440 # video-only or audio-only).
441 ydl = YDL({'format': 'best[height>360]'})
442 self.assertRaises(ExtractorError, ydl.process_ie_result, info_dict.copy())
444 def test_format_selection_issue_10083(self):
445 # See https://github.com/ytdl-org/youtube-dl/issues/10083
447 {'format_id': 'regular', 'height': 360, 'url': TEST_URL},
448 {'format_id': 'video', 'height': 720, 'acodec': 'none', 'url': TEST_URL},
449 {'format_id': 'audio', 'vcodec': 'none', 'url': TEST_URL},
451 info_dict = _make_result(formats)
453 ydl = YDL({'format': 'best[height>360]/bestvideo[height>360]+bestaudio'})
454 ydl.process_ie_result(info_dict.copy())
455 self.assertEqual(ydl.downloaded_info_dicts[0]['format_id'], 'video+audio')
457 def test_invalid_format_specs(self):
458 def assert_syntax_error(format_spec):
459 ydl = YDL({'format': format_spec})
460 info_dict = _make_result([{'format_id': 'foo', 'url': TEST_URL}])
461 self.assertRaises(SyntaxError, ydl.process_ie_result, info_dict)
463 assert_syntax_error('bestvideo,,best')
464 assert_syntax_error('+bestaudio')
465 assert_syntax_error('bestvideo+')
466 assert_syntax_error('/')
468 def test_format_filtering(self):
470 {'format_id': 'A', 'filesize': 500, 'width': 1000},
471 {'format_id': 'B', 'filesize': 1000, 'width': 500},
472 {'format_id': 'C', 'filesize': 1000, 'width': 400},
473 {'format_id': 'D', 'filesize': 2000, 'width': 600},
474 {'format_id': 'E', 'filesize': 3000},
476 {'format_id': 'G', 'filesize': 1000000},
479 f['url'] = 'http://_/'
481 info_dict = _make_result(formats)
483 ydl = YDL({'format': 'best[filesize<3000]'})
484 ydl.process_ie_result(info_dict)
485 downloaded = ydl.downloaded_info_dicts[0]
486 self.assertEqual(downloaded['format_id'], 'D')
488 ydl = YDL({'format': 'best[filesize<=3000]'})
489 ydl.process_ie_result(info_dict)
490 downloaded = ydl.downloaded_info_dicts[0]
491 self.assertEqual(downloaded['format_id'], 'E')
493 ydl = YDL({'format': 'best[filesize <= ? 3000]'})
494 ydl.process_ie_result(info_dict)
495 downloaded = ydl.downloaded_info_dicts[0]
496 self.assertEqual(downloaded['format_id'], 'F')
498 ydl = YDL({'format': 'best [filesize = 1000] [width>450]'})
499 ydl.process_ie_result(info_dict)
500 downloaded = ydl.downloaded_info_dicts[0]
501 self.assertEqual(downloaded['format_id'], 'B')
503 ydl = YDL({'format': 'best [filesize = 1000] [width!=450]'})
504 ydl.process_ie_result(info_dict)
505 downloaded = ydl.downloaded_info_dicts[0]
506 self.assertEqual(downloaded['format_id'], 'C')
508 ydl = YDL({'format': '[filesize>?1]'})
509 ydl.process_ie_result(info_dict)
510 downloaded = ydl.downloaded_info_dicts[0]
511 self.assertEqual(downloaded['format_id'], 'G')
513 ydl = YDL({'format': '[filesize<1M]'})
514 ydl.process_ie_result(info_dict)
515 downloaded = ydl.downloaded_info_dicts[0]
516 self.assertEqual(downloaded['format_id'], 'E')
518 ydl = YDL({'format': '[filesize<1MiB]'})
519 ydl.process_ie_result(info_dict)
520 downloaded = ydl.downloaded_info_dicts[0]
521 self.assertEqual(downloaded['format_id'], 'G')
523 ydl = YDL({'format': 'all[width>=400][width<=600]'})
524 ydl.process_ie_result(info_dict)
525 downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
526 self.assertEqual(downloaded_ids, ['B', 'C', 'D'])
528 ydl = YDL({'format': 'best[height<40]'})
530 ydl.process_ie_result(info_dict)
531 except ExtractorError:
533 self.assertEqual(ydl.downloaded_info_dicts, [])
535 def test_default_format_spec(self):
536 ydl = YDL({'simulate': True})
537 self.assertEqual(ydl._default_format_spec({}), 'bestvideo+bestaudio/best')
540 self.assertEqual(ydl._default_format_spec({'is_live': True}), 'best/bestvideo+bestaudio')
542 ydl = YDL({'simulate': True})
543 self.assertEqual(ydl._default_format_spec({'is_live': True}), 'bestvideo+bestaudio/best')
545 ydl = YDL({'outtmpl': '-'})
546 self.assertEqual(ydl._default_format_spec({}), 'best/bestvideo+bestaudio')
549 self.assertEqual(ydl._default_format_spec({}, download=False), 'bestvideo+bestaudio/best')
550 self.assertEqual(ydl._default_format_spec({'is_live': True}), 'best/bestvideo+bestaudio')
553 class TestYoutubeDL(unittest.TestCase):
554 def test_subtitles(self):
555 def s_formats(lang, autocaption=False):
558 'url': 'http://localhost/video.%s.%s' % (lang, ext),
559 '_auto': autocaption,
560 } for ext in ['vtt', 'srt', 'ass']]
561 subtitles = dict((l, s_formats(l)) for l in ['en', 'fr', 'es'])
562 auto_captions = dict((l, s_formats(l, True)) for l in ['it', 'pt', 'es'])
566 'url': 'http://localhost/video.mp4',
567 'subtitles': subtitles,
568 'automatic_captions': auto_captions,
572 def get_info(params={}):
573 params.setdefault('simulate', True)
575 ydl.report_warning = lambda *args, **kargs: None
576 return ydl.process_video_result(info_dict, download=False)
579 self.assertFalse(result.get('requested_subtitles'))
580 self.assertEqual(result['subtitles'], subtitles)
581 self.assertEqual(result['automatic_captions'], auto_captions)
583 result = get_info({'writesubtitles': True})
584 subs = result['requested_subtitles']
585 self.assertTrue(subs)
586 self.assertEqual(set(subs.keys()), set(['en']))
587 self.assertTrue(subs['en'].get('data') is None)
588 self.assertEqual(subs['en']['ext'], 'ass')
590 result = get_info({'writesubtitles': True, 'subtitlesformat': 'foo/srt'})
591 subs = result['requested_subtitles']
592 self.assertEqual(subs['en']['ext'], 'srt')
594 result = get_info({'writesubtitles': True, 'subtitleslangs': ['es', 'fr', 'it']})
595 subs = result['requested_subtitles']
596 self.assertTrue(subs)
597 self.assertEqual(set(subs.keys()), set(['es', 'fr']))
599 result = get_info({'writesubtitles': True, 'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
600 subs = result['requested_subtitles']
601 self.assertTrue(subs)
602 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
603 self.assertFalse(subs['es']['_auto'])
604 self.assertTrue(subs['pt']['_auto'])
606 result = get_info({'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
607 subs = result['requested_subtitles']
608 self.assertTrue(subs)
609 self.assertEqual(set(subs.keys()), set(['es', 'pt']))
610 self.assertTrue(subs['es']['_auto'])
611 self.assertTrue(subs['pt']['_auto'])
613 def test_add_extra_info(self):
619 'playlist': 'funny videos',
621 YDL.add_extra_info(test_dict, extra_info)
622 self.assertEqual(test_dict['extractor'], 'Foo')
623 self.assertEqual(test_dict['playlist'], 'funny videos')
625 def test_prepare_filename(self):
636 ydl = YoutubeDL({'outtmpl': templ})
637 return ydl.prepare_filename(info)
638 self.assertEqual(fname('%(id)s.%(ext)s'), '1234.mp4')
639 self.assertEqual(fname('%(id)s-%(width)s.%(ext)s'), '1234-NA.mp4')
640 # Replace missing fields with 'NA'
641 self.assertEqual(fname('%(uploader_date)s-%(id)s.%(ext)s'), 'NA-1234.mp4')
642 self.assertEqual(fname('%(height)d.%(ext)s'), '1080.mp4')
643 self.assertEqual(fname('%(height)6d.%(ext)s'), ' 1080.mp4')
644 self.assertEqual(fname('%(height)-6d.%(ext)s'), '1080 .mp4')
645 self.assertEqual(fname('%(height)06d.%(ext)s'), '001080.mp4')
646 self.assertEqual(fname('%(height) 06d.%(ext)s'), ' 01080.mp4')
647 self.assertEqual(fname('%(height) 06d.%(ext)s'), ' 01080.mp4')
648 self.assertEqual(fname('%(height)0 6d.%(ext)s'), ' 01080.mp4')
649 self.assertEqual(fname('%(height)0 6d.%(ext)s'), ' 01080.mp4')
650 self.assertEqual(fname('%(height) 0 6d.%(ext)s'), ' 01080.mp4')
651 self.assertEqual(fname('%%'), '%')
652 self.assertEqual(fname('%%%%'), '%%')
653 self.assertEqual(fname('%%(height)06d.%(ext)s'), '%(height)06d.mp4')
654 self.assertEqual(fname('%(width)06d.%(ext)s'), 'NA.mp4')
655 self.assertEqual(fname('%(width)06d.%%(ext)s'), 'NA.%(ext)s')
656 self.assertEqual(fname('%%(width)06d.%(ext)s'), '%(width)06d.mp4')
657 self.assertEqual(fname('Hello %(title1)s'), 'Hello $PATH')
658 self.assertEqual(fname('Hello %(title2)s'), 'Hello %PATH%')
660 def test_format_note(self):
662 self.assertEqual(ydl._format_note({}), '')
663 assertRegexpMatches(self, ydl._format_note({
666 assertRegexpMatches(self, ydl._format_note({
670 def test_postprocessors(self):
671 filename = 'post-processor-testfile.mp4'
672 audiofile = filename + '.mp3'
674 class SimplePP(PostProcessor):
676 with open(audiofile, 'wt') as f:
678 return [info['filepath']], info
680 def run_pp(params, PP):
681 with open(filename, 'wt') as f:
683 ydl = YoutubeDL(params)
684 ydl.add_post_processor(PP())
685 ydl.post_process(filename, {'filepath': filename})
687 run_pp({'keepvideo': True}, SimplePP)
688 self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
689 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
693 run_pp({'keepvideo': False}, SimplePP)
694 self.assertFalse(os.path.exists(filename), '%s exists' % filename)
695 self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
698 class ModifierPP(PostProcessor):
700 with open(info['filepath'], 'wt') as f:
704 run_pp({'keepvideo': False}, ModifierPP)
705 self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
708 def test_match_filter(self):
709 class FilterYDL(YDL):
710 def __init__(self, *args, **kwargs):
711 super(FilterYDL, self).__init__(*args, **kwargs)
712 self.params['simulate'] = True
714 def process_info(self, info_dict):
715 super(YDL, self).process_info(info_dict)
717 def _match_entry(self, info_dict, incomplete):
718 res = super(FilterYDL, self)._match_entry(info_dict, incomplete)
720 self.downloaded_info_dicts.append(info_dict)
729 'filesize': 10 * 1024,
731 'uploader': "變態妍字幕版 太妍 тест",
732 'creator': "тест ' 123 ' тест--",
740 'description': 'foo',
741 'filesize': 5 * 1024,
743 'uploader': "тест 123",
745 videos = [first, second]
747 def get_videos(filter_=None):
748 ydl = FilterYDL({'match_filter': filter_})
750 ydl.process_ie_result(v, download=True)
751 return [v['id'] for v in ydl.downloaded_info_dicts]
754 self.assertEqual(res, ['1', '2'])
760 return 'Video id is not 1'
762 self.assertEqual(res, ['1'])
764 f = match_filter_func('duration < 30')
766 self.assertEqual(res, ['2'])
768 f = match_filter_func('description = foo')
770 self.assertEqual(res, ['2'])
772 f = match_filter_func('description =? foo')
774 self.assertEqual(res, ['1', '2'])
776 f = match_filter_func('filesize > 5KiB')
778 self.assertEqual(res, ['1'])
780 f = match_filter_func('playlist_id = 42')
782 self.assertEqual(res, ['1'])
784 f = match_filter_func('uploader = "變態妍字幕版 太妍 тест"')
786 self.assertEqual(res, ['1'])
788 f = match_filter_func('uploader != "變態妍字幕版 太妍 тест"')
790 self.assertEqual(res, ['2'])
792 f = match_filter_func('creator = "тест \' 123 \' тест--"')
794 self.assertEqual(res, ['1'])
796 f = match_filter_func("creator = 'тест \\' 123 \\' тест--'")
798 self.assertEqual(res, ['1'])
800 f = match_filter_func(r"creator = 'тест \' 123 \' тест--' & duration > 30")
802 self.assertEqual(res, [])
804 def test_playlist_items_selection(self):
807 'title': compat_str(i),
809 } for i in range(1, 5)]
814 'extractor': 'test:playlist',
815 'extractor_key': 'test:playlist',
816 'webpage_url': 'http://example.com',
819 def get_downloaded_info_dicts(params):
821 # make a deep copy because the dictionary and nested entries
823 ydl.process_ie_result(copy.deepcopy(playlist))
824 return ydl.downloaded_info_dicts
827 return [int(v['id']) for v in get_downloaded_info_dicts(params)]
830 self.assertEqual(result, [1, 2, 3, 4])
832 result = get_ids({'playlistend': 10})
833 self.assertEqual(result, [1, 2, 3, 4])
835 result = get_ids({'playlistend': 2})
836 self.assertEqual(result, [1, 2])
838 result = get_ids({'playliststart': 10})
839 self.assertEqual(result, [])
841 result = get_ids({'playliststart': 2})
842 self.assertEqual(result, [2, 3, 4])
844 result = get_ids({'playlist_items': '2-4'})
845 self.assertEqual(result, [2, 3, 4])
847 result = get_ids({'playlist_items': '2,4'})
848 self.assertEqual(result, [2, 4])
850 result = get_ids({'playlist_items': '10'})
851 self.assertEqual(result, [])
853 result = get_ids({'playlist_items': '3-10'})
854 self.assertEqual(result, [3, 4])
856 result = get_ids({'playlist_items': '2-4,3-4,3'})
857 self.assertEqual(result, [2, 3, 4])
859 # Tests for https://github.com/ytdl-org/youtube-dl/issues/10591
861 result = get_downloaded_info_dicts({'playlist_items': '2-4,3-4,3'})
862 self.assertEqual(result[0]['playlist_index'], 2)
863 self.assertEqual(result[1]['playlist_index'], 3)
865 result = get_downloaded_info_dicts({'playlist_items': '2-4,3-4,3'})
866 self.assertEqual(result[0]['playlist_index'], 2)
867 self.assertEqual(result[1]['playlist_index'], 3)
868 self.assertEqual(result[2]['playlist_index'], 4)
870 result = get_downloaded_info_dicts({'playlist_items': '4,2'})
871 self.assertEqual(result[0]['playlist_index'], 4)
872 self.assertEqual(result[1]['playlist_index'], 2)
875 def test_urlopen_no_file_protocol(self):
876 # see https://github.com/ytdl-org/youtube-dl/issues/8227
878 self.assertRaises(compat_urllib_error.URLError, ydl.urlopen, 'file:///etc/passwd')
880 def test_do_not_override_ie_key_in_url_transparent(self):
883 class Foo1IE(InfoExtractor):
884 _VALID_URL = r'foo1:'
886 def _real_extract(self, url):
888 '_type': 'url_transparent',
891 'title': 'foo1 title',
895 class Foo2IE(InfoExtractor):
896 _VALID_URL = r'foo2:'
898 def _real_extract(self, url):
905 class Foo3IE(InfoExtractor):
906 _VALID_URL = r'foo3:'
908 def _real_extract(self, url):
909 return _make_result([{'url': TEST_URL}], title='foo3 title')
911 ydl.add_info_extractor(Foo1IE(ydl))
912 ydl.add_info_extractor(Foo2IE(ydl))
913 ydl.add_info_extractor(Foo3IE(ydl))
914 ydl.extract_info('foo1:')
915 downloaded = ydl.downloaded_info_dicts[0]
916 self.assertEqual(downloaded['url'], TEST_URL)
917 self.assertEqual(downloaded['title'], 'foo1 title')
918 self.assertEqual(downloaded['id'], 'testid')
919 self.assertEqual(downloaded['extractor'], 'testex')
920 self.assertEqual(downloaded['extractor_key'], 'TestEx')
923 if __name__ == '__main__':