2 # Allow direct execution
7 sys
.path
.insert(0, os
.path
.dirname(os
.path
.dirname(os
.path
.abspath(__file__
))))
13 from test
.helper
import FakeYDL
, assertRegexpMatches
14 from yt_dlp
import YoutubeDL
15 from yt_dlp
.compat
import compat_os_name
, compat_str
16 from yt_dlp
.extractor
import YoutubeIE
17 from yt_dlp
.extractor
.common
import InfoExtractor
18 from yt_dlp
.postprocessor
.common
import PostProcessor
19 from yt_dlp
.utils
import (
27 TEST_URL
= 'http://localhost/sample.mp4'
31 def __init__(self
, *args
, **kwargs
):
32 super().__init
__(*args
, **kwargs
)
33 self
.downloaded_info_dicts
= []
36 def process_info(self
, info_dict
):
37 self
.downloaded_info_dicts
.append(info_dict
.copy())
39 def to_screen(self
, msg
, *args
, **kwargs
):
42 def dl(self
, *args
, **kwargs
):
43 assert False, 'Downloader must not be invoked for test_YoutubeDL'
46 def _make_result(formats
, **kwargs
):
50 'title': 'testttitle',
51 'extractor': 'testex',
52 'extractor_key': 'TestEx',
53 'webpage_url': 'http://example.com/watch?v=shenanigans',
59 class TestFormatSelection(unittest
.TestCase
):
60 def test_prefer_free_formats(self
):
61 # Same resolution => download webm
63 ydl
.params
['prefer_free_formats'] = True
65 {'ext': 'webm', 'height': 460, 'url': TEST_URL}
,
66 {'ext': 'mp4', 'height': 460, 'url': TEST_URL}
,
68 info_dict
= _make_result(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'], 'webm')
75 # Different resolution => download best quality (mp4)
77 ydl
.params
['prefer_free_formats'] = True
79 {'ext': 'webm', 'height': 720, 'url': TEST_URL}
,
80 {'ext': 'mp4', 'height': 1080, 'url': TEST_URL}
,
82 info_dict
['formats'] = formats
84 yie
._sort
_formats
(info_dict
['formats'])
85 ydl
.process_ie_result(info_dict
)
86 downloaded
= ydl
.downloaded_info_dicts
[0]
87 self
.assertEqual(downloaded
['ext'], 'mp4')
89 # No prefer_free_formats => prefer mp4 and webm
91 ydl
.params
['prefer_free_formats'] = False
93 {'ext': 'webm', 'height': 720, 'url': TEST_URL}
,
94 {'ext': 'mp4', 'height': 720, 'url': TEST_URL}
,
95 {'ext': 'flv', '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'], 'mp4')
105 ydl
.params
['prefer_free_formats'] = False
107 {'ext': 'flv', 'height': 720, 'url': TEST_URL}
,
108 {'ext': 'webm', 'height': 720, 'url': TEST_URL}
,
110 info_dict
['formats'] = formats
112 yie
._sort
_formats
(info_dict
['formats'])
113 ydl
.process_ie_result(info_dict
)
114 downloaded
= ydl
.downloaded_info_dicts
[0]
115 self
.assertEqual(downloaded
['ext'], 'webm')
117 def test_format_selection(self
):
119 {'format_id': '35', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL}
,
120 {'format_id': 'example-with-dashes', 'ext': 'webm', 'preference': 1, 'url': TEST_URL}
,
121 {'format_id': '45', 'ext': 'webm', 'preference': 2, 'url': TEST_URL}
,
122 {'format_id': '47', 'ext': 'webm', 'preference': 3, 'url': TEST_URL}
,
123 {'format_id': '2', 'ext': 'flv', 'preference': 4, 'url': TEST_URL}
,
125 info_dict
= _make_result(formats
)
127 def test(inp
, *expected
, multi
=False):
130 'allow_multiple_video_streams': multi
,
131 'allow_multiple_audio_streams': multi
,
133 ydl
.process_ie_result(info_dict
.copy())
134 downloaded
= map(lambda x
: x
['format_id'], ydl
.downloaded_info_dicts
)
135 self
.assertEqual(list(downloaded
), list(expected
))
138 test('20/71/worst', '35')
140 test('webm/mp4', '47')
141 test('3gp/40/mp4', '35')
142 test('example-with-dashes', 'example-with-dashes')
143 test('all', '2', '47', '45', 'example-with-dashes', '35')
144 test('mergeall', '2+47+45+example-with-dashes+35', multi
=True)
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')
225 ydl
= YDL({'format': 'bestvideo[format_id^=dash][format_id$=low]'}
)
226 ydl
.process_ie_result(info_dict
.copy())
227 downloaded
= ydl
.downloaded_info_dicts
[0]
228 self
.assertEqual(downloaded
['format_id'], 'dash-video-low')
231 {'format_id': 'vid-vcodec-dot', 'ext': 'mp4', 'preference': 1, 'vcodec': 'avc1.123456', 'acodec': 'none', 'url': TEST_URL}
,
233 info_dict
= _make_result(formats
)
235 ydl
= YDL({'format': 'bestvideo[vcodec=avc1.123456]'}
)
236 ydl
.process_ie_result(info_dict
.copy())
237 downloaded
= ydl
.downloaded_info_dicts
[0]
238 self
.assertEqual(downloaded
['format_id'], 'vid-vcodec-dot')
240 def test_format_selection_string_ops(self
):
242 {'format_id': 'abc-cba', 'ext': 'mp4', 'url': TEST_URL}
,
243 {'format_id': 'zxc-cxz', 'ext': 'webm', 'url': TEST_URL}
,
245 info_dict
= _make_result(formats
)
248 ydl
= YDL({'format': '[format_id=abc-cba]'}
)
249 ydl
.process_ie_result(info_dict
.copy())
250 downloaded
= ydl
.downloaded_info_dicts
[0]
251 self
.assertEqual(downloaded
['format_id'], 'abc-cba')
253 # does not equal (!=)
254 ydl
= YDL({'format': '[format_id!=abc-cba]'}
)
255 ydl
.process_ie_result(info_dict
.copy())
256 downloaded
= ydl
.downloaded_info_dicts
[0]
257 self
.assertEqual(downloaded
['format_id'], 'zxc-cxz')
259 ydl
= YDL({'format': '[format_id!=abc-cba][format_id!=zxc-cxz]'}
)
260 self
.assertRaises(ExtractorError
, ydl
.process_ie_result
, info_dict
.copy())
263 ydl
= YDL({'format': '[format_id^=abc]'}
)
264 ydl
.process_ie_result(info_dict
.copy())
265 downloaded
= ydl
.downloaded_info_dicts
[0]
266 self
.assertEqual(downloaded
['format_id'], 'abc-cba')
268 # does not start with (!^=)
269 ydl
= YDL({'format': '[format_id!^=abc]'}
)
270 ydl
.process_ie_result(info_dict
.copy())
271 downloaded
= ydl
.downloaded_info_dicts
[0]
272 self
.assertEqual(downloaded
['format_id'], 'zxc-cxz')
274 ydl
= YDL({'format': '[format_id!^=abc][format_id!^=zxc]'}
)
275 self
.assertRaises(ExtractorError
, ydl
.process_ie_result
, info_dict
.copy())
278 ydl
= YDL({'format': '[format_id$=cba]'}
)
279 ydl
.process_ie_result(info_dict
.copy())
280 downloaded
= ydl
.downloaded_info_dicts
[0]
281 self
.assertEqual(downloaded
['format_id'], 'abc-cba')
283 # does not end with (!$=)
284 ydl
= YDL({'format': '[format_id!$=cba]'}
)
285 ydl
.process_ie_result(info_dict
.copy())
286 downloaded
= ydl
.downloaded_info_dicts
[0]
287 self
.assertEqual(downloaded
['format_id'], 'zxc-cxz')
289 ydl
= YDL({'format': '[format_id!$=cba][format_id!$=cxz]'}
)
290 self
.assertRaises(ExtractorError
, ydl
.process_ie_result
, info_dict
.copy())
293 ydl
= YDL({'format': '[format_id*=bc-cb]'}
)
294 ydl
.process_ie_result(info_dict
.copy())
295 downloaded
= ydl
.downloaded_info_dicts
[0]
296 self
.assertEqual(downloaded
['format_id'], 'abc-cba')
298 # does not contain (!*=)
299 ydl
= YDL({'format': '[format_id!*=bc-cb]'}
)
300 ydl
.process_ie_result(info_dict
.copy())
301 downloaded
= ydl
.downloaded_info_dicts
[0]
302 self
.assertEqual(downloaded
['format_id'], 'zxc-cxz')
304 ydl
= YDL({'format': '[format_id!*=abc][format_id!*=zxc]'}
)
305 self
.assertRaises(ExtractorError
, ydl
.process_ie_result
, info_dict
.copy())
307 ydl
= YDL({'format': '[format_id!*=-]'}
)
308 self
.assertRaises(ExtractorError
, ydl
.process_ie_result
, info_dict
.copy())
310 def test_youtube_format_selection(self
):
311 # FIXME: Rewrite in accordance with the new format sorting options
315 '38', '37', '46', '22', '45', '35', '44', '18', '34', '43', '6', '5', '17', '36', '13',
316 # Apple HTTP Live Streaming
317 '96', '95', '94', '93', '92', '132', '151',
319 '85', '84', '102', '83', '101', '82', '100',
321 '137', '248', '136', '247', '135', '246',
322 '245', '244', '134', '243', '133', '242', '160',
324 '141', '172', '140', '171', '139',
327 def format_info(f_id
):
328 info
= YoutubeIE
._formats
[f_id
].copy()
330 # XXX: In real cases InfoExtractor._parse_mpd_formats() fills up 'acodec'
331 # and 'vcodec', while in tests such information is incomplete since
332 # commit a6c2c24479e5f4827ceb06f64d855329c0a6f593
333 # test_YoutubeDL.test_youtube_format_selection is broken without
335 if 'acodec' in info
and 'vcodec' not in info
:
336 info
['vcodec'] = 'none'
337 elif 'vcodec' in info
and 'acodec' not in info
:
338 info
['acodec'] = 'none'
340 info
['format_id'] = f_id
341 info
['url'] = 'url:' + f_id
343 formats_order
= [format_info(f_id
) for f_id
in order
]
345 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
346 ydl
= YDL({'format': 'bestvideo+bestaudio'}
)
348 yie
._sort
_formats
(info_dict
['formats'])
349 ydl
.process_ie_result(info_dict
)
350 downloaded
= ydl
.downloaded_info_dicts
[0]
351 self
.assertEqual(downloaded
['format_id'], '248+172')
352 self
.assertEqual(downloaded
['ext'], 'mp4')
354 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
355 ydl
= YDL({'format': 'bestvideo[height>=999999]+bestaudio/best'}
)
357 yie
._sort
_formats
(info_dict
['formats'])
358 ydl
.process_ie_result(info_dict
)
359 downloaded
= ydl
.downloaded_info_dicts
[0]
360 self
.assertEqual(downloaded
['format_id'], '38')
362 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
363 ydl
= YDL({'format': 'bestvideo/best,bestaudio'}
)
365 yie
._sort
_formats
(info_dict
['formats'])
366 ydl
.process_ie_result(info_dict
)
367 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
368 self
.assertEqual(downloaded_ids
, ['137', '141'])
370 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
371 ydl
= YDL({'format': '(bestvideo[ext=mp4],bestvideo[ext=webm])+bestaudio'}
)
373 yie
._sort
_formats
(info_dict
['formats'])
374 ydl
.process_ie_result(info_dict
)
375 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
376 self
.assertEqual(downloaded_ids
, ['137+141', '248+141'])
378 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
379 ydl
= YDL({'format': '(bestvideo[ext=mp4],bestvideo[ext=webm])[height<=720]+bestaudio'}
)
381 yie
._sort
_formats
(info_dict
['formats'])
382 ydl
.process_ie_result(info_dict
)
383 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
384 self
.assertEqual(downloaded_ids
, ['136+141', '247+141'])
386 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
387 ydl
= YDL({'format': '(bestvideo[ext=none]/bestvideo[ext=webm])+bestaudio'}
)
389 yie
._sort
_formats
(info_dict
['formats'])
390 ydl
.process_ie_result(info_dict
)
391 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
392 self
.assertEqual(downloaded_ids
, ['248+141'])
394 for f1
, f2
in zip(formats_order
, formats_order
[1:]):
395 info_dict
= _make_result([f1
, f2
], extractor
='youtube')
396 ydl
= YDL({'format': 'best/bestvideo'}
)
398 yie
._sort
_formats
(info_dict
['formats'])
399 ydl
.process_ie_result(info_dict
)
400 downloaded
= ydl
.downloaded_info_dicts
[0]
401 self
.assertEqual(downloaded
['format_id'], f1
['format_id'])
403 info_dict
= _make_result([f2
, f1
], extractor
='youtube')
404 ydl
= YDL({'format': 'best/bestvideo'}
)
406 yie
._sort
_formats
(info_dict
['formats'])
407 ydl
.process_ie_result(info_dict
)
408 downloaded
= ydl
.downloaded_info_dicts
[0]
409 self
.assertEqual(downloaded
['format_id'], f1
['format_id'])
411 def test_audio_only_extractor_format_selection(self
):
412 # For extractors with incomplete formats (all formats are audio-only or
413 # video-only) best and worst should fallback to corresponding best/worst
414 # video-only or audio-only formats (as per
415 # https://github.com/ytdl-org/youtube-dl/pull/5556)
417 {'format_id': 'low', 'ext': 'mp3', 'preference': 1, 'vcodec': 'none', 'url': TEST_URL}
,
418 {'format_id': 'high', 'ext': 'mp3', 'preference': 2, 'vcodec': 'none', 'url': TEST_URL}
,
420 info_dict
= _make_result(formats
)
422 ydl
= YDL({'format': 'best'}
)
423 ydl
.process_ie_result(info_dict
.copy())
424 downloaded
= ydl
.downloaded_info_dicts
[0]
425 self
.assertEqual(downloaded
['format_id'], 'high')
427 ydl
= YDL({'format': 'worst'}
)
428 ydl
.process_ie_result(info_dict
.copy())
429 downloaded
= ydl
.downloaded_info_dicts
[0]
430 self
.assertEqual(downloaded
['format_id'], 'low')
432 def test_format_not_available(self
):
434 {'format_id': 'regular', 'ext': 'mp4', 'height': 360, 'url': TEST_URL}
,
435 {'format_id': 'video', 'ext': 'mp4', 'height': 720, 'acodec': 'none', 'url': TEST_URL}
,
437 info_dict
= _make_result(formats
)
439 # This must fail since complete video-audio format does not match filter
440 # and extractor does not provide incomplete only formats (i.e. only
441 # video-only or audio-only).
442 ydl
= YDL({'format': 'best[height>360]'}
)
443 self
.assertRaises(ExtractorError
, ydl
.process_ie_result
, info_dict
.copy())
445 def test_format_selection_issue_10083(self
):
446 # See https://github.com/ytdl-org/youtube-dl/issues/10083
448 {'format_id': 'regular', 'height': 360, 'url': TEST_URL}
,
449 {'format_id': 'video', 'height': 720, 'acodec': 'none', 'url': TEST_URL}
,
450 {'format_id': 'audio', 'vcodec': 'none', 'url': TEST_URL}
,
452 info_dict
= _make_result(formats
)
454 ydl
= YDL({'format': 'best[height>360]/bestvideo[height>360]+bestaudio'}
)
455 ydl
.process_ie_result(info_dict
.copy())
456 self
.assertEqual(ydl
.downloaded_info_dicts
[0]['format_id'], 'video+audio')
458 def test_invalid_format_specs(self
):
459 def assert_syntax_error(format_spec
):
460 self
.assertRaises(SyntaxError, YDL
, {'format': format_spec}
)
462 assert_syntax_error('bestvideo,,best')
463 assert_syntax_error('+bestaudio')
464 assert_syntax_error('bestvideo+')
465 assert_syntax_error('/')
466 assert_syntax_error('[720<height]')
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
, ['D', 'C', 'B'])
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': f
'http://localhost/video.{lang}.{ext}',
559 '_auto': autocaption
,
560 } for ext
in ['vtt', 'srt', 'ass']]
561 subtitles
= {l: s_formats(l) for l in ['en', 'fr', 'es']}
562 auto_captions
= {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
,
570 'webpage_url': 'http://example.com/watch?v=shenanigans',
573 def get_info(params
={}):
574 params
.setdefault('simulate', True)
576 ydl
.report_warning
= lambda *args
, **kargs
: None
577 return ydl
.process_video_result(info_dict
, download
=False)
580 self
.assertFalse(result
.get('requested_subtitles'))
581 self
.assertEqual(result
['subtitles'], subtitles
)
582 self
.assertEqual(result
['automatic_captions'], auto_captions
)
584 result
= get_info({'writesubtitles': True}
)
585 subs
= result
['requested_subtitles']
586 self
.assertTrue(subs
)
587 self
.assertEqual(set(subs
.keys()), {'en'}
)
588 self
.assertTrue(subs
['en'].get('data') is None)
589 self
.assertEqual(subs
['en']['ext'], 'ass')
591 result
= get_info({'writesubtitles': True, 'subtitlesformat': 'foo/srt'}
)
592 subs
= result
['requested_subtitles']
593 self
.assertEqual(subs
['en']['ext'], 'srt')
595 result
= get_info({'writesubtitles': True, 'subtitleslangs': ['es', 'fr', 'it']}
)
596 subs
= result
['requested_subtitles']
597 self
.assertTrue(subs
)
598 self
.assertEqual(set(subs
.keys()), {'es', 'fr'}
)
600 result
= get_info({'writesubtitles': True, 'subtitleslangs': ['all', '-en']}
)
601 subs
= result
['requested_subtitles']
602 self
.assertTrue(subs
)
603 self
.assertEqual(set(subs
.keys()), {'es', 'fr'}
)
605 result
= get_info({'writesubtitles': True, 'subtitleslangs': ['en', 'fr', '-en']}
)
606 subs
= result
['requested_subtitles']
607 self
.assertTrue(subs
)
608 self
.assertEqual(set(subs
.keys()), {'fr'}
)
610 result
= get_info({'writesubtitles': True, 'subtitleslangs': ['-en', 'en']}
)
611 subs
= result
['requested_subtitles']
612 self
.assertTrue(subs
)
613 self
.assertEqual(set(subs
.keys()), {'en'}
)
615 result
= get_info({'writesubtitles': True, 'subtitleslangs': ['e.+']}
)
616 subs
= result
['requested_subtitles']
617 self
.assertTrue(subs
)
618 self
.assertEqual(set(subs
.keys()), {'es', 'en'}
)
620 result
= get_info({'writesubtitles': True, 'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']}
)
621 subs
= result
['requested_subtitles']
622 self
.assertTrue(subs
)
623 self
.assertEqual(set(subs
.keys()), {'es', 'pt'}
)
624 self
.assertFalse(subs
['es']['_auto'])
625 self
.assertTrue(subs
['pt']['_auto'])
627 result
= get_info({'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']}
)
628 subs
= result
['requested_subtitles']
629 self
.assertTrue(subs
)
630 self
.assertEqual(set(subs
.keys()), {'es', 'pt'}
)
631 self
.assertTrue(subs
['es']['_auto'])
632 self
.assertTrue(subs
['pt']['_auto'])
634 def test_add_extra_info(self
):
640 'playlist': 'funny videos',
642 YDL
.add_extra_info(test_dict
, extra_info
)
643 self
.assertEqual(test_dict
['extractor'], 'Foo')
644 self
.assertEqual(test_dict
['playlist'], 'funny videos')
654 'title3': 'foo/bar\\test',
655 'title4': 'foo "bar" test',
657 'timestamp': 1618488000,
660 'playlist_autonumber': 2,
661 '__last_playlist_index': 100,
663 'formats': [{'id': 'id 1'}
, {'id': 'id 2'}
, {'id': 'id 3'}
]
666 def test_prepare_outtmpl_and_filename(self
):
667 def test(tmpl
, expected
, *, info
=None, **params
):
668 params
['outtmpl'] = tmpl
669 ydl
= YoutubeDL(params
)
670 ydl
._num
_downloads
= 1
671 self
.assertEqual(ydl
.validate_outtmpl(tmpl
), None)
673 out
= ydl
.evaluate_outtmpl(tmpl
, info
or self
.outtmpl_info
)
674 fname
= ydl
.prepare_filename(info
or self
.outtmpl_info
)
676 if not isinstance(expected
, (list, tuple)):
677 expected
= (expected
, expected
)
678 for (name
, got
), expect
in zip((('outtmpl', out
), ('filename', fname
)), expected
):
680 self
.assertTrue(expect(got
), f
'Wrong {name} from {tmpl}')
682 self
.assertEqual(got
, expect
, f
'Wrong {name} from {tmpl}')
685 original_infodict
= dict(self
.outtmpl_info
)
686 test('foo.bar', 'foo.bar')
687 original_infodict
['epoch'] = self
.outtmpl_info
.get('epoch')
688 self
.assertTrue(isinstance(original_infodict
['epoch'], int))
689 test('%(epoch)d', int_or_none
)
690 self
.assertEqual(original_infodict
, self
.outtmpl_info
)
692 # Auto-generated fields
693 test('%(id)s.%(ext)s', '1234.mp4')
694 test('%(duration_string)s', ('27:46:40', '27-46-40'))
695 test('%(resolution)s', '1080p')
696 test('%(playlist_index)s', '001')
697 test('%(playlist_autonumber)s', '02')
698 test('%(autonumber)s', '00001')
699 test('%(autonumber+2)03d', '005', autonumber_start
=3)
700 test('%(autonumber)s', '001', autonumber_size
=3)
709 test('%abc%', '%abc%')
710 test('%%(width)06d.%(ext)s', '%(width)06d.mp4')
711 test('%%%(height)s', '%1080')
712 test('%(width)06d.%(ext)s', 'NA.mp4')
713 test('%(width)06d.%%(ext)s', 'NA.%(ext)s')
714 test('%%(width)06d.%(ext)s', '%(width)06d.mp4')
717 test('%(id)s', '_abcd', info
={'id': '_abcd'}
)
718 test('%(some_id)s', '_abcd', info
={'some_id': '_abcd'}
)
719 test('%(formats.0.id)s', '_abcd', info
={'formats': [{'id': '_abcd'}
]})
720 test('%(id)s', '-abcd', info
={'id': '-abcd'}
)
721 test('%(id)s', '.abcd', info
={'id': '.abcd'}
)
722 test('%(id)s', 'ab__cd', info
={'id': 'ab__cd'}
)
723 test('%(id)s', ('ab:cd', 'ab -cd'), info
={'id': 'ab:cd'}
)
724 test('%(id.0)s', '-', info
={'id': '--'}
)
727 self
.assertTrue(isinstance(YoutubeDL
.validate_outtmpl('%(title)'), ValueError))
728 test('%(invalid@tmpl|def)s', 'none', outtmpl_na_placeholder
='none')
732 def expect_same_infodict(out
):
733 got_dict
= json
.loads(out
)
734 for info_field
, expected
in self
.outtmpl_info
.items():
735 self
.assertEqual(got_dict
.get(info_field
), expected
, info_field
)
738 test('%()j', (expect_same_infodict
, str))
741 NA_TEST_OUTTMPL
= '%(uploader_date)s-%(width)d-%(x|def)s-%(id)s.%(ext)s'
742 test(NA_TEST_OUTTMPL
, 'NA-NA-def-1234.mp4')
743 test(NA_TEST_OUTTMPL
, 'none-none-def-1234.mp4', outtmpl_na_placeholder
='none')
744 test(NA_TEST_OUTTMPL
, '--def-1234.mp4', outtmpl_na_placeholder
='')
745 test('%(non_existent.0)s', 'NA')
748 FMT_TEST_OUTTMPL
= '%%(height)%s.%%(ext)s'
749 test(FMT_TEST_OUTTMPL
% 's', '1080.mp4')
750 test(FMT_TEST_OUTTMPL
% 'd', '1080.mp4')
751 test(FMT_TEST_OUTTMPL
% '6d', ' 1080.mp4')
752 test(FMT_TEST_OUTTMPL
% '-6d', '1080 .mp4')
753 test(FMT_TEST_OUTTMPL
% '06d', '001080.mp4')
754 test(FMT_TEST_OUTTMPL
% ' 06d', ' 01080.mp4')
755 test(FMT_TEST_OUTTMPL
% ' 06d', ' 01080.mp4')
756 test(FMT_TEST_OUTTMPL
% '0 6d', ' 01080.mp4')
757 test(FMT_TEST_OUTTMPL
% '0 6d', ' 01080.mp4')
758 test(FMT_TEST_OUTTMPL
% ' 0 6d', ' 01080.mp4')
761 test('%(id)d', '1234')
762 test('%(height)c', '1')
764 test('%(id)d %(id)r', "1234 '1234'")
765 test('%(id)r %(height)r', "'1234' 1080")
766 test('%(ext)s-%(ext|def)d', 'mp4-def')
767 test('%(width|0)04d', '0000')
768 test('a%(width|)d', 'a', outtmpl_na_placeholder
='none')
770 FORMATS
= self
.outtmpl_info
['formats']
771 sanitize
= lambda x
: x
.replace(':', ' -').replace('"', "'").replace('\n', ' ')
773 # Custom type casting
774 test('%(formats.:.id)l', 'id 1, id 2, id 3')
775 test('%(formats.:.id)#l', ('id 1\nid 2\nid 3', 'id 1 id 2 id 3'))
776 test('%(ext)l', 'mp4')
777 test('%(formats.:.id) 18l', ' id 1, id 2, id 3')
778 test('%(formats)j', (json
.dumps(FORMATS
), sanitize(json
.dumps(FORMATS
))))
779 test('%(formats)#j', (json
.dumps(FORMATS
, indent
=4), sanitize(json
.dumps(FORMATS
, indent
=4))))
780 test('%(title5).3B', 'á')
781 test('%(title5)U', 'áéí 𝐀')
782 test('%(title5)#U', 'a\u0301e\u0301i\u0301 𝐀')
783 test('%(title5)+U', 'áéí A')
784 test('%(title5)+#U', 'a\u0301e\u0301i\u0301 A')
785 test('%(height)D', '1k')
786 test('%(filesize)#D', '1Ki')
787 test('%(height)5.2D', ' 1.08k')
788 test('%(title4)#S', 'foo_bar_test')
789 test('%(title4).10S', ('foo \'bar\' ', 'foo \'bar\'' + ('#' if compat_os_name
== 'nt' else ' ')))
790 if compat_os_name
== 'nt':
791 test('%(title4)q', ('"foo \\"bar\\" test"', "'foo _'bar_' test'"))
792 test('%(formats.:.id)#q', ('"id 1" "id 2" "id 3"', "'id 1' 'id 2' 'id 3'"))
793 test('%(formats.0.id)#q', ('"id 1"', "'id 1'"))
795 test('%(title4)q', ('\'foo "bar" test\'', "'foo 'bar' test'"))
796 test('%(formats.:.id)#q', "'id 1' 'id 2' 'id 3'")
797 test('%(formats.0.id)#q', "'id 1'")
799 # Internal formatting
800 test('%(timestamp-1000>%H-%M-%S)s', '11-43-20')
801 test('%(title|%)s %(title|%%)s', '% %%')
802 test('%(id+1-height+3)05d', '00158')
803 test('%(width+100)05d', 'NA')
804 test('%(formats.0) 15s', ('% 15s' % FORMATS
[0], '% 15s' % sanitize(str(FORMATS
[0]))))
805 test('%(formats.0)r', (repr(FORMATS
[0]), sanitize(repr(FORMATS
[0]))))
806 test('%(height.0)03d', '001')
807 test('%(-height.0)04d', '-001')
808 test('%(formats.-1.id)s', FORMATS
[-1]['id'])
809 test('%(formats.0.id.-1)d', FORMATS
[0]['id'][-1])
810 test('%(formats.3)s', 'NA')
811 test('%(formats.:2:-1)r', repr(FORMATS
[:2:-1]))
812 test('%(formats.0.id.-1+id)f', '1235.000000')
813 test('%(formats.0.id.-1+formats.1.id.-1)d', '3')
816 test('%(title,id)s', '1234')
817 test('%(width-100,height+20|def)d', '1100')
818 test('%(width-100,height+width|def)s', 'def')
819 test('%(timestamp-x>%H\\,%M\\,%S,timestamp>%H\\,%M\\,%S)s', '12,00,00')
822 test('%(id&foo)s.bar', 'foo.bar')
823 test('%(title&foo)s.bar', 'NA.bar')
824 test('%(title&foo|baz)s.bar', 'baz.bar')
825 test('%(x,id&foo|baz)s.bar', 'foo.bar')
826 test('%(x,title&foo|baz)s.bar', 'baz.bar')
831 raise self
.assertTrue(False, 'LazyList should not be evaluated till here')
832 test('%(key.4)s', '4', info
={'key': LazyList(gen())}
)
835 test('%(foo|)s-%(bar|)s.%(ext)s', '-.mp4')
836 # test('%(foo|)s.%(ext)s', ('.mp4', '_.mp4')) # fixme
837 # test('%(foo|)s', ('', '_')) # fixme
839 # Environment variable expansion for prepare_filename
840 os
.environ
['__yt_dlp_var'] = 'expanded'
841 envvar
= '%__yt_dlp_var%' if compat_os_name
== 'nt' else '$__yt_dlp_var'
842 test(envvar
, (envvar
, 'expanded'))
843 if compat_os_name
== 'nt':
844 test('%s%', ('%s%', '%s%'))
845 os
.environ
['s'] = 'expanded'
846 test('%s%', ('%s%', 'expanded')) # %s% should be expanded before escaping %s
847 os
.environ
['(test)s'] = 'expanded'
848 test('%(test)s%', ('NA%', 'expanded')) # Environment should take priority over template
850 # Path expansion and escaping
851 test('Hello %(title1)s', 'Hello $PATH')
852 test('Hello %(title2)s', 'Hello %PATH%')
853 test('%(title3)s', ('foo/bar\\test', 'foo_bar_test'))
854 test('folder/%(title3)s', ('folder/foo/bar\\test', 'folder%sfoo_bar_test' % os
.path
.sep
))
856 def test_format_note(self
):
858 self
.assertEqual(ydl
._format
_note
({}), '')
859 assertRegexpMatches(self
, ydl
._format
_note
({
862 assertRegexpMatches(self
, ydl
._format
_note
({
866 def test_postprocessors(self
):
867 filename
= 'post-processor-testfile.mp4'
868 audiofile
= filename
+ '.mp3'
870 class SimplePP(PostProcessor
):
872 with open(audiofile
, 'wt') as f
:
874 return [info
['filepath']], info
876 def run_pp(params
, PP
):
877 with open(filename
, 'wt') as f
:
879 ydl
= YoutubeDL(params
)
880 ydl
.add_post_processor(PP())
881 ydl
.post_process(filename
, {'filepath': filename}
)
883 run_pp({'keepvideo': True}
, SimplePP
)
884 self
.assertTrue(os
.path
.exists(filename
), '%s doesn\'t exist' % filename
)
885 self
.assertTrue(os
.path
.exists(audiofile
), '%s doesn\'t exist' % audiofile
)
889 run_pp({'keepvideo': False}
, SimplePP
)
890 self
.assertFalse(os
.path
.exists(filename
), '%s exists' % filename
)
891 self
.assertTrue(os
.path
.exists(audiofile
), '%s doesn\'t exist' % audiofile
)
894 class ModifierPP(PostProcessor
):
896 with open(info
['filepath'], 'wt') as f
:
900 run_pp({'keepvideo': False}
, ModifierPP
)
901 self
.assertTrue(os
.path
.exists(filename
), '%s doesn\'t exist' % filename
)
904 def test_match_filter(self
):
911 'filesize': 10 * 1024,
913 'uploader': "變態妍字幕版 太妍 тест",
914 'creator': "тест ' 123 ' тест--",
915 'webpage_url': 'http://example.com/watch?v=shenanigans',
923 'description': 'foo',
924 'filesize': 5 * 1024,
926 'uploader': "тест 123",
927 'webpage_url': 'http://example.com/watch?v=SHENANIGANS',
929 videos
= [first
, second
]
931 def get_videos(filter_
=None):
932 ydl
= YDL({'match_filter': filter_, 'simulate': True}
)
934 ydl
.process_ie_result(v
, download
=True)
935 return [v
['id'] for v
in ydl
.downloaded_info_dicts
]
938 self
.assertEqual(res
, ['1', '2'])
940 def f(v
, incomplete
):
944 return 'Video id is not 1'
946 self
.assertEqual(res
, ['1'])
948 f
= match_filter_func('duration < 30')
950 self
.assertEqual(res
, ['2'])
952 f
= match_filter_func('description = foo')
954 self
.assertEqual(res
, ['2'])
956 f
= match_filter_func('description =? foo')
958 self
.assertEqual(res
, ['1', '2'])
960 f
= match_filter_func('filesize > 5KiB')
962 self
.assertEqual(res
, ['1'])
964 f
= match_filter_func('playlist_id = 42')
966 self
.assertEqual(res
, ['1'])
968 f
= match_filter_func('uploader = "變態妍字幕版 太妍 тест"')
970 self
.assertEqual(res
, ['1'])
972 f
= match_filter_func('uploader != "變態妍字幕版 太妍 тест"')
974 self
.assertEqual(res
, ['2'])
976 f
= match_filter_func('creator = "тест \' 123 \' тест--"')
978 self
.assertEqual(res
, ['1'])
980 f
= match_filter_func("creator = 'тест \\' 123 \\' тест--'")
982 self
.assertEqual(res
, ['1'])
984 f
= match_filter_func(r
"creator = 'тест \' 123 \' тест--' & duration > 30")
986 self
.assertEqual(res
, [])
988 def test_playlist_items_selection(self
):
989 INDICES
, PAGE_SIZE
= list(range(1, 11)), 3
991 def entry(i
, evaluated
):
999 def pagedlist_entries(evaluated
):
1001 start
= PAGE_SIZE
* n
1002 for i
in INDICES
[start
: start
+ PAGE_SIZE
]:
1003 yield entry(i
, evaluated
)
1004 return OnDemandPagedList(page_func
, PAGE_SIZE
)
1007 return (i
+ PAGE_SIZE
- 1) // PAGE_SIZE
1009 def generator_entries(evaluated
):
1011 yield entry(i
, evaluated
)
1013 def list_entries(evaluated
):
1014 return list(generator_entries(evaluated
))
1016 def lazylist_entries(evaluated
):
1017 return LazyList(generator_entries(evaluated
))
1019 def get_downloaded_info_dicts(params
, entries
):
1021 ydl
.process_ie_result({
1022 '_type': 'playlist',
1024 'extractor': 'test:playlist',
1025 'extractor_key': 'test:playlist',
1026 'webpage_url': 'http://example.com',
1029 return ydl
.downloaded_info_dicts
1031 def test_selection(params
, expected_ids
, evaluate_all
=False):
1032 expected_ids
= list(expected_ids
)
1034 generator_eval
= pagedlist_eval
= INDICES
1035 elif not expected_ids
:
1036 generator_eval
= pagedlist_eval
= []
1038 generator_eval
= INDICES
[0: max(expected_ids
)]
1039 pagedlist_eval
= INDICES
[PAGE_SIZE
* page_num(min(expected_ids
)) - PAGE_SIZE
:
1040 PAGE_SIZE
* page_num(max(expected_ids
))]
1042 for name
, func
, expected_eval
in (
1043 ('list', list_entries
, INDICES
),
1044 ('Generator', generator_entries
, generator_eval
),
1045 # ('LazyList', lazylist_entries, generator_eval), # Generator and LazyList follow the exact same code path
1046 ('PagedList', pagedlist_entries
, pagedlist_eval
),
1049 entries
= func(evaluated
)
1050 results
= [(v
['playlist_autonumber'] - 1, (int(v
['id']), v
['playlist_index']))
1051 for v
in get_downloaded_info_dicts(params
, entries
)]
1052 self
.assertEqual(results
, list(enumerate(zip(expected_ids
, expected_ids
))), f
'Entries of {name} for {params}')
1053 self
.assertEqual(sorted(evaluated
), expected_eval
, f
'Evaluation of {name} for {params}')
1054 test_selection({}, INDICES
)
1055 test_selection({'playlistend': 20}
, INDICES
, True)
1056 test_selection({'playlistend': 2}
, INDICES
[:2])
1057 test_selection({'playliststart': 11}
, [], True)
1058 test_selection({'playliststart': 2}
, INDICES
[1:])
1059 test_selection({'playlist_items': '2-4'}
, INDICES
[1:4])
1060 test_selection({'playlist_items': '2,4'}
, [2, 4])
1061 test_selection({'playlist_items': '20'}
, [], True)
1062 test_selection({'playlist_items': '0'}
, [])
1064 # Tests for https://github.com/ytdl-org/youtube-dl/issues/10591
1065 test_selection({'playlist_items': '2-4,3-4,3'}
, [2, 3, 4])
1066 test_selection({'playlist_items': '4,2'}
, [4, 2])
1068 # Tests for https://github.com/yt-dlp/yt-dlp/issues/720
1069 # https://github.com/yt-dlp/yt-dlp/issues/302
1070 test_selection({'playlistreverse': True}
, INDICES
[::-1])
1071 test_selection({'playliststart': 2, 'playlistreverse': True}
, INDICES
[:0:-1])
1072 test_selection({'playlist_items': '2,4', 'playlistreverse': True}
, [4, 2])
1073 test_selection({'playlist_items': '4,2'}
, [4, 2])
1075 # Tests for --playlist-items start:end:step
1076 test_selection({'playlist_items': ':'}
, INDICES
, True)
1077 test_selection({'playlist_items': '::1'}
, INDICES
, True)
1078 test_selection({'playlist_items': '::-1'}
, INDICES
[::-1], True)
1079 test_selection({'playlist_items': ':6'}
, INDICES
[:6])
1080 test_selection({'playlist_items': ':-6'}
, INDICES
[:-5], True)
1081 test_selection({'playlist_items': '-1:6:-2'}
, INDICES
[:4:-2], True)
1082 test_selection({'playlist_items': '9:-6:-2'}
, INDICES
[8:3:-2], True)
1084 test_selection({'playlist_items': '1:inf:2'}
, INDICES
[::2], True)
1085 test_selection({'playlist_items': '-2:inf'}
, INDICES
[-2:], True)
1086 test_selection({'playlist_items': ':inf:-1'}
, [], True)
1087 test_selection({'playlist_items': '0-2:2'}
, [2])
1088 test_selection({'playlist_items': '1-:2'}
, INDICES
[::2], True)
1089 test_selection({'playlist_items': '0--2:2'}
, INDICES
[1:-1:2], True)
1091 test_selection({'playlist_items': '10::3'}
, [10], True)
1092 test_selection({'playlist_items': '-1::3'}
, [10], True)
1093 test_selection({'playlist_items': '11::3'}
, [], True)
1094 test_selection({'playlist_items': '-15::2'}
, INDICES
[1::2], True)
1095 test_selection({'playlist_items': '-15::15'}
, [], True)
1097 def test_urlopen_no_file_protocol(self
):
1098 # see https://github.com/ytdl-org/youtube-dl/issues/8227
1100 self
.assertRaises(urllib
.error
.URLError
, ydl
.urlopen
, 'file:///etc/passwd')
1102 def test_do_not_override_ie_key_in_url_transparent(self
):
1105 class Foo1IE(InfoExtractor
):
1106 _VALID_URL
= r
'foo1:'
1108 def _real_extract(self
, url
):
1110 '_type': 'url_transparent',
1113 'title': 'foo1 title',
1117 class Foo2IE(InfoExtractor
):
1118 _VALID_URL
= r
'foo2:'
1120 def _real_extract(self
, url
):
1127 class Foo3IE(InfoExtractor
):
1128 _VALID_URL
= r
'foo3:'
1130 def _real_extract(self
, url
):
1131 return _make_result([{'url': TEST_URL}
], title
='foo3 title')
1133 ydl
.add_info_extractor(Foo1IE(ydl
))
1134 ydl
.add_info_extractor(Foo2IE(ydl
))
1135 ydl
.add_info_extractor(Foo3IE(ydl
))
1136 ydl
.extract_info('foo1:')
1137 downloaded
= ydl
.downloaded_info_dicts
[0]
1138 self
.assertEqual(downloaded
['url'], TEST_URL
)
1139 self
.assertEqual(downloaded
['title'], 'foo1 title')
1140 self
.assertEqual(downloaded
['id'], 'testid')
1141 self
.assertEqual(downloaded
['extractor'], 'testex')
1142 self
.assertEqual(downloaded
['extractor_key'], 'TestEx')
1144 # Test case for https://github.com/ytdl-org/youtube-dl/issues/27064
1145 def test_ignoreerrors_for_playlist_with_url_transparent_iterable_entries(self
):
1148 def __init__(self
, *args
, **kwargs
):
1149 super().__init
__(*args
, **kwargs
)
1151 def trouble(self
, s
, tb
=None):
1156 'ignoreerrors': True,
1159 class VideoIE(InfoExtractor
):
1160 _VALID_URL
= r
'video:(?P<id>\d+)'
1162 def _real_extract(self
, url
):
1163 video_id
= self
._match
_id
(url
)
1165 'format_id': 'default',
1169 raise ExtractorError('foo')
1172 'format_id': 'extra',
1177 'title': 'Video %s' % video_id
,
1181 class PlaylistIE(InfoExtractor
):
1182 _VALID_URL
= r
'playlist:'
1186 video_id
= compat_str(n
)
1188 '_type': 'url_transparent',
1189 'ie_key': VideoIE
.ie_key(),
1191 'url': 'video:%s' % video_id
,
1192 'title': 'Video Transparent %s' % video_id
,
1195 def _real_extract(self
, url
):
1196 return self
.playlist_result(self
._entries
())
1198 ydl
.add_info_extractor(VideoIE(ydl
))
1199 ydl
.add_info_extractor(PlaylistIE(ydl
))
1200 info
= ydl
.extract_info('playlist:')
1201 entries
= info
['entries']
1202 self
.assertEqual(len(entries
), 3)
1203 self
.assertTrue(entries
[0] is None)
1204 self
.assertTrue(entries
[1] is None)
1205 self
.assertEqual(len(ydl
.downloaded_info_dicts
), 1)
1206 downloaded
= ydl
.downloaded_info_dicts
[0]
1207 entries
[2].pop('requested_downloads', None)
1208 self
.assertEqual(entries
[2], downloaded
)
1209 self
.assertEqual(downloaded
['url'], TEST_URL
)
1210 self
.assertEqual(downloaded
['title'], 'Video Transparent 2')
1211 self
.assertEqual(downloaded
['id'], '2')
1212 self
.assertEqual(downloaded
['extractor'], 'Video')
1213 self
.assertEqual(downloaded
['extractor_key'], 'Video')
1216 if __name__
== '__main__':