3 # Allow direct execution
8 sys
.path
.insert(0, os
.path
.dirname(os
.path
.dirname(os
.path
.abspath(__file__
))))
15 from test
.helper
import FakeYDL
, assertRegexpMatches
16 from yt_dlp
import YoutubeDL
17 from yt_dlp
.compat
import compat_os_name
18 from yt_dlp
.extractor
import YoutubeIE
19 from yt_dlp
.extractor
.common
import InfoExtractor
20 from yt_dlp
.postprocessor
.common
import PostProcessor
21 from yt_dlp
.utils
import (
29 TEST_URL
= 'http://localhost/sample.mp4'
33 def __init__(self
, *args
, **kwargs
):
34 super().__init
__(*args
, **kwargs
)
35 self
.downloaded_info_dicts
= []
38 def process_info(self
, info_dict
):
39 self
.downloaded_info_dicts
.append(info_dict
.copy())
41 def to_screen(self
, msg
, *args
, **kwargs
):
44 def dl(self
, *args
, **kwargs
):
45 assert False, 'Downloader must not be invoked for test_YoutubeDL'
48 def _make_result(formats
, **kwargs
):
52 'title': 'testttitle',
53 'extractor': 'testex',
54 'extractor_key': 'TestEx',
55 'webpage_url': 'http://example.com/watch?v=shenanigans',
61 class TestFormatSelection(unittest
.TestCase
):
62 def test_prefer_free_formats(self
):
63 # Same resolution => download webm
65 ydl
.params
['prefer_free_formats'] = True
67 {'ext': 'webm', 'height': 460, 'url': TEST_URL}
,
68 {'ext': 'mp4', 'height': 460, 'url': TEST_URL}
,
70 info_dict
= _make_result(formats
)
72 yie
._sort
_formats
(info_dict
['formats'])
73 ydl
.process_ie_result(info_dict
)
74 downloaded
= ydl
.downloaded_info_dicts
[0]
75 self
.assertEqual(downloaded
['ext'], 'webm')
77 # Different resolution => download best quality (mp4)
79 ydl
.params
['prefer_free_formats'] = True
81 {'ext': 'webm', 'height': 720, 'url': TEST_URL}
,
82 {'ext': 'mp4', 'height': 1080, 'url': TEST_URL}
,
84 info_dict
['formats'] = formats
86 yie
._sort
_formats
(info_dict
['formats'])
87 ydl
.process_ie_result(info_dict
)
88 downloaded
= ydl
.downloaded_info_dicts
[0]
89 self
.assertEqual(downloaded
['ext'], 'mp4')
91 # No prefer_free_formats => prefer mp4 and webm
93 ydl
.params
['prefer_free_formats'] = False
95 {'ext': 'webm', 'height': 720, 'url': TEST_URL}
,
96 {'ext': 'mp4', 'height': 720, 'url': TEST_URL}
,
97 {'ext': 'flv', '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'], 'mp4')
107 ydl
.params
['prefer_free_formats'] = False
109 {'ext': 'flv', 'height': 720, 'url': TEST_URL}
,
110 {'ext': 'webm', 'height': 720, 'url': TEST_URL}
,
112 info_dict
['formats'] = formats
114 yie
._sort
_formats
(info_dict
['formats'])
115 ydl
.process_ie_result(info_dict
)
116 downloaded
= ydl
.downloaded_info_dicts
[0]
117 self
.assertEqual(downloaded
['ext'], 'webm')
119 def test_format_selection(self
):
121 {'format_id': '35', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL}
,
122 {'format_id': 'example-with-dashes', 'ext': 'webm', 'preference': 1, 'url': TEST_URL}
,
123 {'format_id': '45', 'ext': 'webm', 'preference': 2, 'url': TEST_URL}
,
124 {'format_id': '47', 'ext': 'webm', 'preference': 3, 'url': TEST_URL}
,
125 {'format_id': '2', 'ext': 'flv', 'preference': 4, 'url': TEST_URL}
,
127 info_dict
= _make_result(formats
)
129 def test(inp
, *expected
, multi
=False):
132 'allow_multiple_video_streams': multi
,
133 'allow_multiple_audio_streams': multi
,
135 ydl
.process_ie_result(info_dict
.copy())
136 downloaded
= map(lambda x
: x
['format_id'], ydl
.downloaded_info_dicts
)
137 self
.assertEqual(list(downloaded
), list(expected
))
140 test('20/71/worst', '35')
142 test('webm/mp4', '47')
143 test('3gp/40/mp4', '35')
144 test('example-with-dashes', 'example-with-dashes')
145 test('all', '2', '47', '45', 'example-with-dashes', '35')
146 test('mergeall', '2+47+45+example-with-dashes+35', multi
=True)
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
):
313 # FIXME: Rewrite in accordance with the new format sorting options
317 '38', '37', '46', '22', '45', '35', '44', '18', '34', '43', '6', '5', '17', '36', '13',
318 # Apple HTTP Live Streaming
319 '96', '95', '94', '93', '92', '132', '151',
321 '85', '84', '102', '83', '101', '82', '100',
323 '137', '248', '136', '247', '135', '246',
324 '245', '244', '134', '243', '133', '242', '160',
326 '141', '172', '140', '171', '139',
329 def format_info(f_id
):
330 info
= YoutubeIE
._formats
[f_id
].copy()
332 # XXX: In real cases InfoExtractor._parse_mpd_formats() fills up 'acodec'
333 # and 'vcodec', while in tests such information is incomplete since
334 # commit a6c2c24479e5f4827ceb06f64d855329c0a6f593
335 # test_YoutubeDL.test_youtube_format_selection is broken without
337 if 'acodec' in info
and 'vcodec' not in info
:
338 info
['vcodec'] = 'none'
339 elif 'vcodec' in info
and 'acodec' not in info
:
340 info
['acodec'] = 'none'
342 info
['format_id'] = f_id
343 info
['url'] = 'url:' + f_id
345 formats_order
= [format_info(f_id
) for f_id
in order
]
347 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
348 ydl
= YDL({'format': 'bestvideo+bestaudio'}
)
350 yie
._sort
_formats
(info_dict
['formats'])
351 ydl
.process_ie_result(info_dict
)
352 downloaded
= ydl
.downloaded_info_dicts
[0]
353 self
.assertEqual(downloaded
['format_id'], '248+172')
354 self
.assertEqual(downloaded
['ext'], 'mp4')
356 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
357 ydl
= YDL({'format': 'bestvideo[height>=999999]+bestaudio/best'}
)
359 yie
._sort
_formats
(info_dict
['formats'])
360 ydl
.process_ie_result(info_dict
)
361 downloaded
= ydl
.downloaded_info_dicts
[0]
362 self
.assertEqual(downloaded
['format_id'], '38')
364 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
365 ydl
= YDL({'format': 'bestvideo/best,bestaudio'}
)
367 yie
._sort
_formats
(info_dict
['formats'])
368 ydl
.process_ie_result(info_dict
)
369 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
370 self
.assertEqual(downloaded_ids
, ['137', '141'])
372 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
373 ydl
= YDL({'format': '(bestvideo[ext=mp4],bestvideo[ext=webm])+bestaudio'}
)
375 yie
._sort
_formats
(info_dict
['formats'])
376 ydl
.process_ie_result(info_dict
)
377 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
378 self
.assertEqual(downloaded_ids
, ['137+141', '248+141'])
380 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
381 ydl
= YDL({'format': '(bestvideo[ext=mp4],bestvideo[ext=webm])[height<=720]+bestaudio'}
)
383 yie
._sort
_formats
(info_dict
['formats'])
384 ydl
.process_ie_result(info_dict
)
385 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
386 self
.assertEqual(downloaded_ids
, ['136+141', '247+141'])
388 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
389 ydl
= YDL({'format': '(bestvideo[ext=none]/bestvideo[ext=webm])+bestaudio'}
)
391 yie
._sort
_formats
(info_dict
['formats'])
392 ydl
.process_ie_result(info_dict
)
393 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
394 self
.assertEqual(downloaded_ids
, ['248+141'])
396 for f1
, f2
in zip(formats_order
, formats_order
[1:]):
397 info_dict
= _make_result([f1
, f2
], extractor
='youtube')
398 ydl
= YDL({'format': 'best/bestvideo'}
)
400 yie
._sort
_formats
(info_dict
['formats'])
401 ydl
.process_ie_result(info_dict
)
402 downloaded
= ydl
.downloaded_info_dicts
[0]
403 self
.assertEqual(downloaded
['format_id'], f1
['format_id'])
405 info_dict
= _make_result([f2
, f1
], extractor
='youtube')
406 ydl
= YDL({'format': 'best/bestvideo'}
)
408 yie
._sort
_formats
(info_dict
['formats'])
409 ydl
.process_ie_result(info_dict
)
410 downloaded
= ydl
.downloaded_info_dicts
[0]
411 self
.assertEqual(downloaded
['format_id'], f1
['format_id'])
413 def test_audio_only_extractor_format_selection(self
):
414 # For extractors with incomplete formats (all formats are audio-only or
415 # video-only) best and worst should fallback to corresponding best/worst
416 # video-only or audio-only formats (as per
417 # https://github.com/ytdl-org/youtube-dl/pull/5556)
419 {'format_id': 'low', 'ext': 'mp3', 'preference': 1, 'vcodec': 'none', 'url': TEST_URL}
,
420 {'format_id': 'high', 'ext': 'mp3', 'preference': 2, 'vcodec': 'none', 'url': TEST_URL}
,
422 info_dict
= _make_result(formats
)
424 ydl
= YDL({'format': 'best'}
)
425 ydl
.process_ie_result(info_dict
.copy())
426 downloaded
= ydl
.downloaded_info_dicts
[0]
427 self
.assertEqual(downloaded
['format_id'], 'high')
429 ydl
= YDL({'format': 'worst'}
)
430 ydl
.process_ie_result(info_dict
.copy())
431 downloaded
= ydl
.downloaded_info_dicts
[0]
432 self
.assertEqual(downloaded
['format_id'], 'low')
434 def test_format_not_available(self
):
436 {'format_id': 'regular', 'ext': 'mp4', 'height': 360, 'url': TEST_URL}
,
437 {'format_id': 'video', 'ext': 'mp4', 'height': 720, 'acodec': 'none', 'url': TEST_URL}
,
439 info_dict
= _make_result(formats
)
441 # This must fail since complete video-audio format does not match filter
442 # and extractor does not provide incomplete only formats (i.e. only
443 # video-only or audio-only).
444 ydl
= YDL({'format': 'best[height>360]'}
)
445 self
.assertRaises(ExtractorError
, ydl
.process_ie_result
, info_dict
.copy())
447 def test_format_selection_issue_10083(self
):
448 # See https://github.com/ytdl-org/youtube-dl/issues/10083
450 {'format_id': 'regular', 'height': 360, 'url': TEST_URL}
,
451 {'format_id': 'video', 'height': 720, 'acodec': 'none', 'url': TEST_URL}
,
452 {'format_id': 'audio', 'vcodec': 'none', 'url': TEST_URL}
,
454 info_dict
= _make_result(formats
)
456 ydl
= YDL({'format': 'best[height>360]/bestvideo[height>360]+bestaudio'}
)
457 ydl
.process_ie_result(info_dict
.copy())
458 self
.assertEqual(ydl
.downloaded_info_dicts
[0]['format_id'], 'video+audio')
460 def test_invalid_format_specs(self
):
461 def assert_syntax_error(format_spec
):
462 self
.assertRaises(SyntaxError, YDL
, {'format': format_spec}
)
464 assert_syntax_error('bestvideo,,best')
465 assert_syntax_error('+bestaudio')
466 assert_syntax_error('bestvideo+')
467 assert_syntax_error('/')
468 assert_syntax_error('[720<height]')
470 def test_format_filtering(self
):
472 {'format_id': 'A', 'filesize': 500, 'width': 1000}
,
473 {'format_id': 'B', 'filesize': 1000, 'width': 500}
,
474 {'format_id': 'C', 'filesize': 1000, 'width': 400}
,
475 {'format_id': 'D', 'filesize': 2000, 'width': 600}
,
476 {'format_id': 'E', 'filesize': 3000}
,
478 {'format_id': 'G', 'filesize': 1000000}
,
481 f
['url'] = 'http://_/'
483 info_dict
= _make_result(formats
)
485 ydl
= YDL({'format': 'best[filesize<3000]'}
)
486 ydl
.process_ie_result(info_dict
)
487 downloaded
= ydl
.downloaded_info_dicts
[0]
488 self
.assertEqual(downloaded
['format_id'], 'D')
490 ydl
= YDL({'format': 'best[filesize<=3000]'}
)
491 ydl
.process_ie_result(info_dict
)
492 downloaded
= ydl
.downloaded_info_dicts
[0]
493 self
.assertEqual(downloaded
['format_id'], 'E')
495 ydl
= YDL({'format': 'best[filesize <= ? 3000]'}
)
496 ydl
.process_ie_result(info_dict
)
497 downloaded
= ydl
.downloaded_info_dicts
[0]
498 self
.assertEqual(downloaded
['format_id'], 'F')
500 ydl
= YDL({'format': 'best [filesize = 1000] [width>450]'}
)
501 ydl
.process_ie_result(info_dict
)
502 downloaded
= ydl
.downloaded_info_dicts
[0]
503 self
.assertEqual(downloaded
['format_id'], 'B')
505 ydl
= YDL({'format': 'best [filesize = 1000] [width!=450]'}
)
506 ydl
.process_ie_result(info_dict
)
507 downloaded
= ydl
.downloaded_info_dicts
[0]
508 self
.assertEqual(downloaded
['format_id'], 'C')
510 ydl
= YDL({'format': '[filesize>?1]'}
)
511 ydl
.process_ie_result(info_dict
)
512 downloaded
= ydl
.downloaded_info_dicts
[0]
513 self
.assertEqual(downloaded
['format_id'], 'G')
515 ydl
= YDL({'format': '[filesize<1M]'}
)
516 ydl
.process_ie_result(info_dict
)
517 downloaded
= ydl
.downloaded_info_dicts
[0]
518 self
.assertEqual(downloaded
['format_id'], 'E')
520 ydl
= YDL({'format': '[filesize<1MiB]'}
)
521 ydl
.process_ie_result(info_dict
)
522 downloaded
= ydl
.downloaded_info_dicts
[0]
523 self
.assertEqual(downloaded
['format_id'], 'G')
525 ydl
= YDL({'format': 'all[width>=400][width<=600]'}
)
526 ydl
.process_ie_result(info_dict
)
527 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
528 self
.assertEqual(downloaded_ids
, ['D', 'C', 'B'])
530 ydl
= YDL({'format': 'best[height<40]'}
)
532 ydl
.process_ie_result(info_dict
)
533 except ExtractorError
:
535 self
.assertEqual(ydl
.downloaded_info_dicts
, [])
537 def test_default_format_spec(self
):
538 ydl
= YDL({'simulate': True}
)
539 self
.assertEqual(ydl
._default
_format
_spec
({}), 'bestvideo*+bestaudio/best')
542 self
.assertEqual(ydl
._default
_format
_spec
({'is_live': True}
), 'best/bestvideo+bestaudio')
544 ydl
= YDL({'simulate': True}
)
545 self
.assertEqual(ydl
._default
_format
_spec
({'is_live': True}
), 'bestvideo*+bestaudio/best')
547 ydl
= YDL({'outtmpl': '-'}
)
548 self
.assertEqual(ydl
._default
_format
_spec
({}), 'best/bestvideo+bestaudio')
551 self
.assertEqual(ydl
._default
_format
_spec
({}, download
=False), 'bestvideo*+bestaudio/best')
552 self
.assertEqual(ydl
._default
_format
_spec
({'is_live': True}
), 'best/bestvideo+bestaudio')
555 class TestYoutubeDL(unittest
.TestCase
):
556 def test_subtitles(self
):
557 def s_formats(lang
, autocaption
=False):
560 'url': f
'http://localhost/video.{lang}.{ext}',
561 '_auto': autocaption
,
562 } for ext
in ['vtt', 'srt', 'ass']]
563 subtitles
= {l: s_formats(l) for l in ['en', 'fr', 'es']}
564 auto_captions
= {l: s_formats(l, True) for l in ['it', 'pt', 'es']}
568 'url': 'http://localhost/video.mp4',
569 'subtitles': subtitles
,
570 'automatic_captions': auto_captions
,
572 'webpage_url': 'http://example.com/watch?v=shenanigans',
575 def get_info(params
={}):
576 params
.setdefault('simulate', True)
578 ydl
.report_warning
= lambda *args
, **kargs
: None
579 return ydl
.process_video_result(info_dict
, download
=False)
582 self
.assertFalse(result
.get('requested_subtitles'))
583 self
.assertEqual(result
['subtitles'], subtitles
)
584 self
.assertEqual(result
['automatic_captions'], auto_captions
)
586 result
= get_info({'writesubtitles': True}
)
587 subs
= result
['requested_subtitles']
588 self
.assertTrue(subs
)
589 self
.assertEqual(set(subs
.keys()), {'en'}
)
590 self
.assertTrue(subs
['en'].get('data') is None)
591 self
.assertEqual(subs
['en']['ext'], 'ass')
593 result
= get_info({'writesubtitles': True, 'subtitlesformat': 'foo/srt'}
)
594 subs
= result
['requested_subtitles']
595 self
.assertEqual(subs
['en']['ext'], 'srt')
597 result
= get_info({'writesubtitles': True, 'subtitleslangs': ['es', 'fr', 'it']}
)
598 subs
= result
['requested_subtitles']
599 self
.assertTrue(subs
)
600 self
.assertEqual(set(subs
.keys()), {'es', 'fr'}
)
602 result
= get_info({'writesubtitles': True, 'subtitleslangs': ['all', '-en']}
)
603 subs
= result
['requested_subtitles']
604 self
.assertTrue(subs
)
605 self
.assertEqual(set(subs
.keys()), {'es', 'fr'}
)
607 result
= get_info({'writesubtitles': True, 'subtitleslangs': ['en', 'fr', '-en']}
)
608 subs
= result
['requested_subtitles']
609 self
.assertTrue(subs
)
610 self
.assertEqual(set(subs
.keys()), {'fr'}
)
612 result
= get_info({'writesubtitles': True, 'subtitleslangs': ['-en', 'en']}
)
613 subs
= result
['requested_subtitles']
614 self
.assertTrue(subs
)
615 self
.assertEqual(set(subs
.keys()), {'en'}
)
617 result
= get_info({'writesubtitles': True, 'subtitleslangs': ['e.+']}
)
618 subs
= result
['requested_subtitles']
619 self
.assertTrue(subs
)
620 self
.assertEqual(set(subs
.keys()), {'es', 'en'}
)
622 result
= get_info({'writesubtitles': True, 'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']}
)
623 subs
= result
['requested_subtitles']
624 self
.assertTrue(subs
)
625 self
.assertEqual(set(subs
.keys()), {'es', 'pt'}
)
626 self
.assertFalse(subs
['es']['_auto'])
627 self
.assertTrue(subs
['pt']['_auto'])
629 result
= get_info({'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']}
)
630 subs
= result
['requested_subtitles']
631 self
.assertTrue(subs
)
632 self
.assertEqual(set(subs
.keys()), {'es', 'pt'}
)
633 self
.assertTrue(subs
['es']['_auto'])
634 self
.assertTrue(subs
['pt']['_auto'])
636 def test_add_extra_info(self
):
642 'playlist': 'funny videos',
644 YDL
.add_extra_info(test_dict
, extra_info
)
645 self
.assertEqual(test_dict
['extractor'], 'Foo')
646 self
.assertEqual(test_dict
['playlist'], 'funny videos')
656 'title3': 'foo/bar\\test',
657 'title4': 'foo "bar" test',
659 'timestamp': 1618488000,
662 'playlist_autonumber': 2,
663 '__last_playlist_index': 100,
665 'formats': [{'id': 'id 1'}
, {'id': 'id 2'}
, {'id': 'id 3'}
]
668 def test_prepare_outtmpl_and_filename(self
):
669 def test(tmpl
, expected
, *, info
=None, **params
):
670 params
['outtmpl'] = tmpl
671 ydl
= FakeYDL(params
)
672 ydl
._num
_downloads
= 1
673 self
.assertEqual(ydl
.validate_outtmpl(tmpl
), None)
675 out
= ydl
.evaluate_outtmpl(tmpl
, info
or self
.outtmpl_info
)
676 fname
= ydl
.prepare_filename(info
or self
.outtmpl_info
)
678 if not isinstance(expected
, (list, tuple)):
679 expected
= (expected
, expected
)
680 for (name
, got
), expect
in zip((('outtmpl', out
), ('filename', fname
)), expected
):
682 self
.assertTrue(expect(got
), f
'Wrong {name} from {tmpl}')
684 self
.assertEqual(got
, expect
, f
'Wrong {name} from {tmpl}')
687 original_infodict
= dict(self
.outtmpl_info
)
688 test('foo.bar', 'foo.bar')
689 original_infodict
['epoch'] = self
.outtmpl_info
.get('epoch')
690 self
.assertTrue(isinstance(original_infodict
['epoch'], int))
691 test('%(epoch)d', int_or_none
)
692 self
.assertEqual(original_infodict
, self
.outtmpl_info
)
694 # Auto-generated fields
695 test('%(id)s.%(ext)s', '1234.mp4')
696 test('%(duration_string)s', ('27:46:40', '27-46-40'))
697 test('%(resolution)s', '1080p')
698 test('%(playlist_index)s', '001')
699 test('%(playlist_autonumber)s', '02')
700 test('%(autonumber)s', '00001')
701 test('%(autonumber+2)03d', '005', autonumber_start
=3)
702 test('%(autonumber)s', '001', autonumber_size
=3)
711 test('%abc%', '%abc%')
712 test('%%(width)06d.%(ext)s', '%(width)06d.mp4')
713 test('%%%(height)s', '%1080')
714 test('%(width)06d.%(ext)s', 'NA.mp4')
715 test('%(width)06d.%%(ext)s', 'NA.%(ext)s')
716 test('%%(width)06d.%(ext)s', '%(width)06d.mp4')
719 test('%(id)s', '_abcd', info
={'id': '_abcd'}
)
720 test('%(some_id)s', '_abcd', info
={'some_id': '_abcd'}
)
721 test('%(formats.0.id)s', '_abcd', info
={'formats': [{'id': '_abcd'}
]})
722 test('%(id)s', '-abcd', info
={'id': '-abcd'}
)
723 test('%(id)s', '.abcd', info
={'id': '.abcd'}
)
724 test('%(id)s', 'ab__cd', info
={'id': 'ab__cd'}
)
725 test('%(id)s', ('ab:cd', 'ab:cd'), info
={'id': 'ab:cd'}
)
726 test('%(id.0)s', '-', info
={'id': '--'}
)
729 self
.assertTrue(isinstance(YoutubeDL
.validate_outtmpl('%(title)'), ValueError))
730 test('%(invalid@tmpl|def)s', 'none', outtmpl_na_placeholder
='none')
734 def expect_same_infodict(out
):
735 got_dict
= json
.loads(out
)
736 for info_field
, expected
in self
.outtmpl_info
.items():
737 self
.assertEqual(got_dict
.get(info_field
), expected
, info_field
)
740 test('%()j', (expect_same_infodict
, str))
743 NA_TEST_OUTTMPL
= '%(uploader_date)s-%(width)d-%(x|def)s-%(id)s.%(ext)s'
744 test(NA_TEST_OUTTMPL
, 'NA-NA-def-1234.mp4')
745 test(NA_TEST_OUTTMPL
, 'none-none-def-1234.mp4', outtmpl_na_placeholder
='none')
746 test(NA_TEST_OUTTMPL
, '--def-1234.mp4', outtmpl_na_placeholder
='')
747 test('%(non_existent.0)s', 'NA')
750 FMT_TEST_OUTTMPL
= '%%(height)%s.%%(ext)s'
751 test(FMT_TEST_OUTTMPL
% 's', '1080.mp4')
752 test(FMT_TEST_OUTTMPL
% 'd', '1080.mp4')
753 test(FMT_TEST_OUTTMPL
% '6d', ' 1080.mp4')
754 test(FMT_TEST_OUTTMPL
% '-6d', '1080 .mp4')
755 test(FMT_TEST_OUTTMPL
% '06d', '001080.mp4')
756 test(FMT_TEST_OUTTMPL
% ' 06d', ' 01080.mp4')
757 test(FMT_TEST_OUTTMPL
% ' 06d', ' 01080.mp4')
758 test(FMT_TEST_OUTTMPL
% '0 6d', ' 01080.mp4')
759 test(FMT_TEST_OUTTMPL
% '0 6d', ' 01080.mp4')
760 test(FMT_TEST_OUTTMPL
% ' 0 6d', ' 01080.mp4')
763 test('%(id)d', '1234')
764 test('%(height)c', '1')
766 test('%(id)d %(id)r', "1234 '1234'")
767 test('%(id)r %(height)r', "'1234' 1080")
768 test('%(ext)s-%(ext|def)d', 'mp4-def')
769 test('%(width|0)04d', '0000')
770 test('a%(width|)d', 'a', outtmpl_na_placeholder
='none')
772 FORMATS
= self
.outtmpl_info
['formats']
773 sanitize
= lambda x
: x
.replace(':', ':').replace('"', """).replace('\n', ' ')
775 # Custom type casting
776 test('%(formats.:.id)l', 'id 1, id 2, id 3')
777 test('%(formats.:.id)#l', ('id 1\nid 2\nid 3', 'id 1 id 2 id 3'))
778 test('%(ext)l', 'mp4')
779 test('%(formats.:.id) 18l', ' id 1, id 2, id 3')
780 test('%(formats)j', (json
.dumps(FORMATS
), sanitize(json
.dumps(FORMATS
))))
781 test('%(formats)#j', (json
.dumps(FORMATS
, indent
=4), sanitize(json
.dumps(FORMATS
, indent
=4))))
782 test('%(title5).3B', 'á')
783 test('%(title5)U', 'áéí 𝐀')
784 test('%(title5)#U', 'a\u0301e\u0301i\u0301 𝐀')
785 test('%(title5)+U', 'áéí A')
786 test('%(title5)+#U', 'a\u0301e\u0301i\u0301 A')
787 test('%(height)D', '1k')
788 test('%(filesize)#D', '1Ki')
789 test('%(height)5.2D', ' 1.08k')
790 test('%(title4)#S', 'foo_bar_test')
791 test('%(title4).10S', ('foo "bar" ', 'foo "bar"' + ('#' if compat_os_name
== 'nt' else ' ')))
792 if compat_os_name
== 'nt':
793 test('%(title4)q', ('"foo \\"bar\\" test"', ""foo ⧹"bar⧹" test""))
794 test('%(formats.:.id)#q', ('"id 1" "id 2" "id 3"', '"id 1" "id 2" "id 3"'))
795 test('%(formats.0.id)#q', ('"id 1"', '"id 1"'))
797 test('%(title4)q', ('\'foo "bar" test\'', '\'foo "bar" test\''))
798 test('%(formats.:.id)#q', "'id 1' 'id 2' 'id 3'")
799 test('%(formats.0.id)#q', "'id 1'")
801 # Internal formatting
802 test('%(timestamp-1000>%H-%M-%S)s', '11-43-20')
803 test('%(title|%)s %(title|%%)s', '% %%')
804 test('%(id+1-height+3)05d', '00158')
805 test('%(width+100)05d', 'NA')
806 test('%(formats.0) 15s', ('% 15s' % FORMATS
[0], '% 15s' % sanitize(str(FORMATS
[0]))))
807 test('%(formats.0)r', (repr(FORMATS
[0]), sanitize(repr(FORMATS
[0]))))
808 test('%(height.0)03d', '001')
809 test('%(-height.0)04d', '-001')
810 test('%(formats.-1.id)s', FORMATS
[-1]['id'])
811 test('%(formats.0.id.-1)d', FORMATS
[0]['id'][-1])
812 test('%(formats.3)s', 'NA')
813 test('%(formats.:2:-1)r', repr(FORMATS
[:2:-1]))
814 test('%(formats.0.id.-1+id)f', '1235.000000')
815 test('%(formats.0.id.-1+formats.1.id.-1)d', '3')
818 test('%(title,id)s', '1234')
819 test('%(width-100,height+20|def)d', '1100')
820 test('%(width-100,height+width|def)s', 'def')
821 test('%(timestamp-x>%H\\,%M\\,%S,timestamp>%H\\,%M\\,%S)s', '12,00,00')
824 test('%(id&foo)s.bar', 'foo.bar')
825 test('%(title&foo)s.bar', 'NA.bar')
826 test('%(title&foo|baz)s.bar', 'baz.bar')
827 test('%(x,id&foo|baz)s.bar', 'foo.bar')
828 test('%(x,title&foo|baz)s.bar', 'baz.bar')
833 raise self
.assertTrue(False, 'LazyList should not be evaluated till here')
834 test('%(key.4)s', '4', info
={'key': LazyList(gen())}
)
837 test('%(foo|)s-%(bar|)s.%(ext)s', '-.mp4')
838 # test('%(foo|)s.%(ext)s', ('.mp4', '_.mp4')) # fixme
839 # test('%(foo|)s', ('', '_')) # fixme
841 # Environment variable expansion for prepare_filename
842 os
.environ
['__yt_dlp_var'] = 'expanded'
843 envvar
= '%__yt_dlp_var%' if compat_os_name
== 'nt' else '$__yt_dlp_var'
844 test(envvar
, (envvar
, 'expanded'))
845 if compat_os_name
== 'nt':
846 test('%s%', ('%s%', '%s%'))
847 os
.environ
['s'] = 'expanded'
848 test('%s%', ('%s%', 'expanded')) # %s% should be expanded before escaping %s
849 os
.environ
['(test)s'] = 'expanded'
850 test('%(test)s%', ('NA%', 'expanded')) # Environment should take priority over template
852 # Path expansion and escaping
853 test('Hello %(title1)s', 'Hello $PATH')
854 test('Hello %(title2)s', 'Hello %PATH%')
855 test('%(title3)s', ('foo/bar\\test', 'foo⧸bar⧹test'))
856 test('folder/%(title3)s', ('folder/foo/bar\\test', 'folder%sfoo⧸bar⧹test' % os
.path
.sep
))
858 def test_format_note(self
):
860 self
.assertEqual(ydl
._format
_note
({}), '')
861 assertRegexpMatches(self
, ydl
._format
_note
({
864 assertRegexpMatches(self
, ydl
._format
_note
({
868 def test_postprocessors(self
):
869 filename
= 'post-processor-testfile.mp4'
870 audiofile
= filename
+ '.mp3'
872 class SimplePP(PostProcessor
):
874 with open(audiofile
, 'wt') as f
:
876 return [info
['filepath']], info
878 def run_pp(params
, PP
):
879 with open(filename
, 'wt') as f
:
881 ydl
= YoutubeDL(params
)
882 ydl
.add_post_processor(PP())
883 ydl
.post_process(filename
, {'filepath': filename}
)
885 run_pp({'keepvideo': True}
, SimplePP
)
886 self
.assertTrue(os
.path
.exists(filename
), '%s doesn\'t exist' % filename
)
887 self
.assertTrue(os
.path
.exists(audiofile
), '%s doesn\'t exist' % audiofile
)
891 run_pp({'keepvideo': False}
, SimplePP
)
892 self
.assertFalse(os
.path
.exists(filename
), '%s exists' % filename
)
893 self
.assertTrue(os
.path
.exists(audiofile
), '%s doesn\'t exist' % audiofile
)
896 class ModifierPP(PostProcessor
):
898 with open(info
['filepath'], 'wt') as f
:
902 run_pp({'keepvideo': False}
, ModifierPP
)
903 self
.assertTrue(os
.path
.exists(filename
), '%s doesn\'t exist' % filename
)
906 def test_match_filter(self
):
913 'filesize': 10 * 1024,
915 'uploader': "變態妍字幕版 太妍 тест",
916 'creator': "тест ' 123 ' тест--",
917 'webpage_url': 'http://example.com/watch?v=shenanigans',
925 'description': 'foo',
926 'filesize': 5 * 1024,
928 'uploader': "тест 123",
929 'webpage_url': 'http://example.com/watch?v=SHENANIGANS',
931 videos
= [first
, second
]
933 def get_videos(filter_
=None):
934 ydl
= YDL({'match_filter': filter_, 'simulate': True}
)
936 ydl
.process_ie_result(v
, download
=True)
937 return [v
['id'] for v
in ydl
.downloaded_info_dicts
]
940 self
.assertEqual(res
, ['1', '2'])
942 def f(v
, incomplete
):
946 return 'Video id is not 1'
948 self
.assertEqual(res
, ['1'])
950 f
= match_filter_func('duration < 30')
952 self
.assertEqual(res
, ['2'])
954 f
= match_filter_func('description = foo')
956 self
.assertEqual(res
, ['2'])
958 f
= match_filter_func('description =? foo')
960 self
.assertEqual(res
, ['1', '2'])
962 f
= match_filter_func('filesize > 5KiB')
964 self
.assertEqual(res
, ['1'])
966 f
= match_filter_func('playlist_id = 42')
968 self
.assertEqual(res
, ['1'])
970 f
= match_filter_func('uploader = "變態妍字幕版 太妍 тест"')
972 self
.assertEqual(res
, ['1'])
974 f
= match_filter_func('uploader != "變態妍字幕版 太妍 тест"')
976 self
.assertEqual(res
, ['2'])
978 f
= match_filter_func('creator = "тест \' 123 \' тест--"')
980 self
.assertEqual(res
, ['1'])
982 f
= match_filter_func("creator = 'тест \\' 123 \\' тест--'")
984 self
.assertEqual(res
, ['1'])
986 f
= match_filter_func(r
"creator = 'тест \' 123 \' тест--' & duration > 30")
988 self
.assertEqual(res
, [])
990 def test_playlist_items_selection(self
):
991 INDICES
, PAGE_SIZE
= list(range(1, 11)), 3
993 def entry(i
, evaluated
):
1001 def pagedlist_entries(evaluated
):
1003 start
= PAGE_SIZE
* n
1004 for i
in INDICES
[start
: start
+ PAGE_SIZE
]:
1005 yield entry(i
, evaluated
)
1006 return OnDemandPagedList(page_func
, PAGE_SIZE
)
1009 return (i
+ PAGE_SIZE
- 1) // PAGE_SIZE
1011 def generator_entries(evaluated
):
1013 yield entry(i
, evaluated
)
1015 def list_entries(evaluated
):
1016 return list(generator_entries(evaluated
))
1018 def lazylist_entries(evaluated
):
1019 return LazyList(generator_entries(evaluated
))
1021 def get_downloaded_info_dicts(params
, entries
):
1023 ydl
.process_ie_result({
1024 '_type': 'playlist',
1026 'extractor': 'test:playlist',
1027 'extractor_key': 'test:playlist',
1028 'webpage_url': 'http://example.com',
1031 return ydl
.downloaded_info_dicts
1033 def test_selection(params
, expected_ids
, evaluate_all
=False):
1034 expected_ids
= list(expected_ids
)
1036 generator_eval
= pagedlist_eval
= INDICES
1037 elif not expected_ids
:
1038 generator_eval
= pagedlist_eval
= []
1040 generator_eval
= INDICES
[0: max(expected_ids
)]
1041 pagedlist_eval
= INDICES
[PAGE_SIZE
* page_num(min(expected_ids
)) - PAGE_SIZE
:
1042 PAGE_SIZE
* page_num(max(expected_ids
))]
1044 for name
, func
, expected_eval
in (
1045 ('list', list_entries
, INDICES
),
1046 ('Generator', generator_entries
, generator_eval
),
1047 # ('LazyList', lazylist_entries, generator_eval), # Generator and LazyList follow the exact same code path
1048 ('PagedList', pagedlist_entries
, pagedlist_eval
),
1051 entries
= func(evaluated
)
1052 results
= [(v
['playlist_autonumber'] - 1, (int(v
['id']), v
['playlist_index']))
1053 for v
in get_downloaded_info_dicts(params
, entries
)]
1054 self
.assertEqual(results
, list(enumerate(zip(expected_ids
, expected_ids
))), f
'Entries of {name} for {params}')
1055 self
.assertEqual(sorted(evaluated
), expected_eval
, f
'Evaluation of {name} for {params}')
1057 test_selection({}, INDICES
)
1058 test_selection({'playlistend': 20}
, INDICES
, True)
1059 test_selection({'playlistend': 2}
, INDICES
[:2])
1060 test_selection({'playliststart': 11}
, [], True)
1061 test_selection({'playliststart': 2}
, INDICES
[1:])
1062 test_selection({'playlist_items': '2-4'}
, INDICES
[1:4])
1063 test_selection({'playlist_items': '2,4'}
, [2, 4])
1064 test_selection({'playlist_items': '20'}
, [], True)
1065 test_selection({'playlist_items': '0'}
, [])
1067 # Tests for https://github.com/ytdl-org/youtube-dl/issues/10591
1068 test_selection({'playlist_items': '2-4,3-4,3'}
, [2, 3, 4])
1069 test_selection({'playlist_items': '4,2'}
, [4, 2])
1071 # Tests for https://github.com/yt-dlp/yt-dlp/issues/720
1072 # https://github.com/yt-dlp/yt-dlp/issues/302
1073 test_selection({'playlistreverse': True}
, INDICES
[::-1])
1074 test_selection({'playliststart': 2, 'playlistreverse': True}
, INDICES
[:0:-1])
1075 test_selection({'playlist_items': '2,4', 'playlistreverse': True}
, [4, 2])
1076 test_selection({'playlist_items': '4,2'}
, [4, 2])
1078 # Tests for --playlist-items start:end:step
1079 test_selection({'playlist_items': ':'}
, INDICES
, True)
1080 test_selection({'playlist_items': '::1'}
, INDICES
, True)
1081 test_selection({'playlist_items': '::-1'}
, INDICES
[::-1], True)
1082 test_selection({'playlist_items': ':6'}
, INDICES
[:6])
1083 test_selection({'playlist_items': ':-6'}
, INDICES
[:-5], True)
1084 test_selection({'playlist_items': '-1:6:-2'}
, INDICES
[:4:-2], True)
1085 test_selection({'playlist_items': '9:-6:-2'}
, INDICES
[8:3:-2], True)
1087 test_selection({'playlist_items': '1:inf:2'}
, INDICES
[::2], True)
1088 test_selection({'playlist_items': '-2:inf'}
, INDICES
[-2:], True)
1089 test_selection({'playlist_items': ':inf:-1'}
, [], True)
1090 test_selection({'playlist_items': '0-2:2'}
, [2])
1091 test_selection({'playlist_items': '1-:2'}
, INDICES
[::2], True)
1092 test_selection({'playlist_items': '0--2:2'}
, INDICES
[1:-1:2], True)
1094 test_selection({'playlist_items': '10::3'}
, [10], True)
1095 test_selection({'playlist_items': '-1::3'}
, [10], True)
1096 test_selection({'playlist_items': '11::3'}
, [], True)
1097 test_selection({'playlist_items': '-15::2'}
, INDICES
[1::2], True)
1098 test_selection({'playlist_items': '-15::15'}
, [], True)
1100 def test_urlopen_no_file_protocol(self
):
1101 # see https://github.com/ytdl-org/youtube-dl/issues/8227
1103 self
.assertRaises(urllib
.error
.URLError
, ydl
.urlopen
, 'file:///etc/passwd')
1105 def test_do_not_override_ie_key_in_url_transparent(self
):
1108 class Foo1IE(InfoExtractor
):
1109 _VALID_URL
= r
'foo1:'
1111 def _real_extract(self
, url
):
1113 '_type': 'url_transparent',
1116 'title': 'foo1 title',
1120 class Foo2IE(InfoExtractor
):
1121 _VALID_URL
= r
'foo2:'
1123 def _real_extract(self
, url
):
1130 class Foo3IE(InfoExtractor
):
1131 _VALID_URL
= r
'foo3:'
1133 def _real_extract(self
, url
):
1134 return _make_result([{'url': TEST_URL}
], title
='foo3 title')
1136 ydl
.add_info_extractor(Foo1IE(ydl
))
1137 ydl
.add_info_extractor(Foo2IE(ydl
))
1138 ydl
.add_info_extractor(Foo3IE(ydl
))
1139 ydl
.extract_info('foo1:')
1140 downloaded
= ydl
.downloaded_info_dicts
[0]
1141 self
.assertEqual(downloaded
['url'], TEST_URL
)
1142 self
.assertEqual(downloaded
['title'], 'foo1 title')
1143 self
.assertEqual(downloaded
['id'], 'testid')
1144 self
.assertEqual(downloaded
['extractor'], 'testex')
1145 self
.assertEqual(downloaded
['extractor_key'], 'TestEx')
1147 # Test case for https://github.com/ytdl-org/youtube-dl/issues/27064
1148 def test_ignoreerrors_for_playlist_with_url_transparent_iterable_entries(self
):
1151 def __init__(self
, *args
, **kwargs
):
1152 super().__init
__(*args
, **kwargs
)
1154 def trouble(self
, s
, tb
=None):
1159 'ignoreerrors': True,
1162 class VideoIE(InfoExtractor
):
1163 _VALID_URL
= r
'video:(?P<id>\d+)'
1165 def _real_extract(self
, url
):
1166 video_id
= self
._match
_id
(url
)
1168 'format_id': 'default',
1172 raise ExtractorError('foo')
1175 'format_id': 'extra',
1180 'title': 'Video %s' % video_id
,
1184 class PlaylistIE(InfoExtractor
):
1185 _VALID_URL
= r
'playlist:'
1191 '_type': 'url_transparent',
1192 'ie_key': VideoIE
.ie_key(),
1194 'url': 'video:%s' % video_id
,
1195 'title': 'Video Transparent %s' % video_id
,
1198 def _real_extract(self
, url
):
1199 return self
.playlist_result(self
._entries
())
1201 ydl
.add_info_extractor(VideoIE(ydl
))
1202 ydl
.add_info_extractor(PlaylistIE(ydl
))
1203 info
= ydl
.extract_info('playlist:')
1204 entries
= info
['entries']
1205 self
.assertEqual(len(entries
), 3)
1206 self
.assertTrue(entries
[0] is None)
1207 self
.assertTrue(entries
[1] is None)
1208 self
.assertEqual(len(ydl
.downloaded_info_dicts
), 1)
1209 downloaded
= ydl
.downloaded_info_dicts
[0]
1210 entries
[2].pop('requested_downloads', None)
1211 self
.assertEqual(entries
[2], downloaded
)
1212 self
.assertEqual(downloaded
['url'], TEST_URL
)
1213 self
.assertEqual(downloaded
['title'], 'Video Transparent 2')
1214 self
.assertEqual(downloaded
['id'], '2')
1215 self
.assertEqual(downloaded
['extractor'], 'Video')
1216 self
.assertEqual(downloaded
['extractor_key'], 'Video')
1219 if __name__
== '__main__':