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 yt_dlp
import YoutubeDL
16 from yt_dlp
.compat
import compat_str
, compat_urllib_error
17 from yt_dlp
.extractor
import YoutubeIE
18 from yt_dlp
.extractor
.common
import InfoExtractor
19 from yt_dlp
.postprocessor
.common
import PostProcessor
20 from yt_dlp
.utils
import ExtractorError
, float_or_none
, 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 info_dict
.pop('__original_infodict', None)
33 self
.downloaded_info_dicts
.append(info_dict
)
35 def to_screen(self
, msg
):
39 def _make_result(formats
, **kwargs
):
43 'title': 'testttitle',
44 'extractor': 'testex',
45 'extractor_key': 'TestEx',
46 'webpage_url': 'http://example.com/watch?v=shenanigans',
52 class TestFormatSelection(unittest
.TestCase
):
53 def test_prefer_free_formats(self
):
54 # Same resolution => download webm
56 ydl
.params
['prefer_free_formats'] = True
58 {'ext': 'webm', 'height': 460, 'url': TEST_URL}
,
59 {'ext': 'mp4', 'height': 460, 'url': TEST_URL}
,
61 info_dict
= _make_result(formats
)
63 yie
._sort
_formats
(info_dict
['formats'])
64 ydl
.process_ie_result(info_dict
)
65 downloaded
= ydl
.downloaded_info_dicts
[0]
66 self
.assertEqual(downloaded
['ext'], 'webm')
68 # Different resolution => download best quality (mp4)
70 ydl
.params
['prefer_free_formats'] = True
72 {'ext': 'webm', 'height': 720, 'url': TEST_URL}
,
73 {'ext': 'mp4', 'height': 1080, 'url': TEST_URL}
,
75 info_dict
['formats'] = formats
77 yie
._sort
_formats
(info_dict
['formats'])
78 ydl
.process_ie_result(info_dict
)
79 downloaded
= ydl
.downloaded_info_dicts
[0]
80 self
.assertEqual(downloaded
['ext'], 'mp4')
82 # No prefer_free_formats => prefer mp4 and webm
84 ydl
.params
['prefer_free_formats'] = False
86 {'ext': 'webm', 'height': 720, 'url': TEST_URL}
,
87 {'ext': 'mp4', 'height': 720, 'url': TEST_URL}
,
88 {'ext': 'flv', 'height': 720, 'url': TEST_URL}
,
90 info_dict
['formats'] = formats
92 yie
._sort
_formats
(info_dict
['formats'])
93 ydl
.process_ie_result(info_dict
)
94 downloaded
= ydl
.downloaded_info_dicts
[0]
95 self
.assertEqual(downloaded
['ext'], 'mp4')
98 ydl
.params
['prefer_free_formats'] = False
100 {'ext': 'flv', 'height': 720, 'url': TEST_URL}
,
101 {'ext': 'webm', 'height': 720, 'url': TEST_URL}
,
103 info_dict
['formats'] = formats
105 yie
._sort
_formats
(info_dict
['formats'])
106 ydl
.process_ie_result(info_dict
)
107 downloaded
= ydl
.downloaded_info_dicts
[0]
108 self
.assertEqual(downloaded
['ext'], 'webm')
110 def test_format_selection(self
):
112 {'format_id': '35', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL}
,
113 {'format_id': 'example-with-dashes', 'ext': 'webm', 'preference': 1, 'url': TEST_URL}
,
114 {'format_id': '45', 'ext': 'webm', 'preference': 2, 'url': TEST_URL}
,
115 {'format_id': '47', 'ext': 'webm', 'preference': 3, 'url': TEST_URL}
,
116 {'format_id': '2', 'ext': 'flv', 'preference': 4, 'url': TEST_URL}
,
118 info_dict
= _make_result(formats
)
120 ydl
= YDL({'format': '20/47'}
)
121 ydl
.process_ie_result(info_dict
.copy())
122 downloaded
= ydl
.downloaded_info_dicts
[0]
123 self
.assertEqual(downloaded
['format_id'], '47')
125 ydl
= YDL({'format': '20/71/worst'}
)
126 ydl
.process_ie_result(info_dict
.copy())
127 downloaded
= ydl
.downloaded_info_dicts
[0]
128 self
.assertEqual(downloaded
['format_id'], '35')
131 ydl
.process_ie_result(info_dict
.copy())
132 downloaded
= ydl
.downloaded_info_dicts
[0]
133 self
.assertEqual(downloaded
['format_id'], '2')
135 ydl
= YDL({'format': 'webm/mp4'}
)
136 ydl
.process_ie_result(info_dict
.copy())
137 downloaded
= ydl
.downloaded_info_dicts
[0]
138 self
.assertEqual(downloaded
['format_id'], '47')
140 ydl
= YDL({'format': '3gp/40/mp4'}
)
141 ydl
.process_ie_result(info_dict
.copy())
142 downloaded
= ydl
.downloaded_info_dicts
[0]
143 self
.assertEqual(downloaded
['format_id'], '35')
145 ydl
= YDL({'format': 'example-with-dashes'}
)
146 ydl
.process_ie_result(info_dict
.copy())
147 downloaded
= ydl
.downloaded_info_dicts
[0]
148 self
.assertEqual(downloaded
['format_id'], 'example-with-dashes')
150 def test_format_selection_audio(self
):
152 {'format_id': 'audio-low', 'ext': 'webm', 'preference': 1, 'vcodec': 'none', 'url': TEST_URL}
,
153 {'format_id': 'audio-mid', 'ext': 'webm', 'preference': 2, 'vcodec': 'none', 'url': TEST_URL}
,
154 {'format_id': 'audio-high', 'ext': 'flv', 'preference': 3, 'vcodec': 'none', 'url': TEST_URL}
,
155 {'format_id': 'vid', 'ext': 'mp4', 'preference': 4, 'url': TEST_URL}
,
157 info_dict
= _make_result(formats
)
159 ydl
= YDL({'format': 'bestaudio'}
)
160 ydl
.process_ie_result(info_dict
.copy())
161 downloaded
= ydl
.downloaded_info_dicts
[0]
162 self
.assertEqual(downloaded
['format_id'], 'audio-high')
164 ydl
= YDL({'format': 'worstaudio'}
)
165 ydl
.process_ie_result(info_dict
.copy())
166 downloaded
= ydl
.downloaded_info_dicts
[0]
167 self
.assertEqual(downloaded
['format_id'], 'audio-low')
170 {'format_id': 'vid-low', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL}
,
171 {'format_id': 'vid-high', 'ext': 'mp4', 'preference': 2, 'url': TEST_URL}
,
173 info_dict
= _make_result(formats
)
175 ydl
= YDL({'format': 'bestaudio/worstaudio/best'}
)
176 ydl
.process_ie_result(info_dict
.copy())
177 downloaded
= ydl
.downloaded_info_dicts
[0]
178 self
.assertEqual(downloaded
['format_id'], 'vid-high')
180 def test_format_selection_audio_exts(self
):
182 {'format_id': 'mp3-64', 'ext': 'mp3', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'}
,
183 {'format_id': 'ogg-64', 'ext': 'ogg', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'}
,
184 {'format_id': 'aac-64', 'ext': 'aac', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'}
,
185 {'format_id': 'mp3-32', 'ext': 'mp3', 'abr': 32, 'url': 'http://_', 'vcodec': 'none'}
,
186 {'format_id': 'aac-32', 'ext': 'aac', 'abr': 32, 'url': 'http://_', 'vcodec': 'none'}
,
189 info_dict
= _make_result(formats
)
190 ydl
= YDL({'format': 'best'}
)
192 ie
._sort
_formats
(info_dict
['formats'])
193 ydl
.process_ie_result(copy
.deepcopy(info_dict
))
194 downloaded
= ydl
.downloaded_info_dicts
[0]
195 self
.assertEqual(downloaded
['format_id'], 'aac-64')
197 ydl
= YDL({'format': 'mp3'}
)
199 ie
._sort
_formats
(info_dict
['formats'])
200 ydl
.process_ie_result(copy
.deepcopy(info_dict
))
201 downloaded
= ydl
.downloaded_info_dicts
[0]
202 self
.assertEqual(downloaded
['format_id'], 'mp3-64')
204 ydl
= YDL({'prefer_free_formats': True}
)
206 ie
._sort
_formats
(info_dict
['formats'])
207 ydl
.process_ie_result(copy
.deepcopy(info_dict
))
208 downloaded
= ydl
.downloaded_info_dicts
[0]
209 self
.assertEqual(downloaded
['format_id'], 'ogg-64')
211 def test_format_selection_video(self
):
213 {'format_id': 'dash-video-low', 'ext': 'mp4', 'preference': 1, 'acodec': 'none', 'url': TEST_URL}
,
214 {'format_id': 'dash-video-high', 'ext': 'mp4', 'preference': 2, 'acodec': 'none', 'url': TEST_URL}
,
215 {'format_id': 'vid', 'ext': 'mp4', 'preference': 3, 'url': TEST_URL}
,
217 info_dict
= _make_result(formats
)
219 ydl
= YDL({'format': 'bestvideo'}
)
220 ydl
.process_ie_result(info_dict
.copy())
221 downloaded
= ydl
.downloaded_info_dicts
[0]
222 self
.assertEqual(downloaded
['format_id'], 'dash-video-high')
224 ydl
= YDL({'format': 'worstvideo'}
)
225 ydl
.process_ie_result(info_dict
.copy())
226 downloaded
= ydl
.downloaded_info_dicts
[0]
227 self
.assertEqual(downloaded
['format_id'], 'dash-video-low')
229 ydl
= YDL({'format': 'bestvideo[format_id^=dash][format_id$=low]'}
)
230 ydl
.process_ie_result(info_dict
.copy())
231 downloaded
= ydl
.downloaded_info_dicts
[0]
232 self
.assertEqual(downloaded
['format_id'], 'dash-video-low')
235 {'format_id': 'vid-vcodec-dot', 'ext': 'mp4', 'preference': 1, 'vcodec': 'avc1.123456', 'acodec': 'none', 'url': TEST_URL}
,
237 info_dict
= _make_result(formats
)
239 ydl
= YDL({'format': 'bestvideo[vcodec=avc1.123456]'}
)
240 ydl
.process_ie_result(info_dict
.copy())
241 downloaded
= ydl
.downloaded_info_dicts
[0]
242 self
.assertEqual(downloaded
['format_id'], 'vid-vcodec-dot')
244 def test_format_selection_string_ops(self
):
246 {'format_id': 'abc-cba', 'ext': 'mp4', 'url': TEST_URL}
,
247 {'format_id': 'zxc-cxz', 'ext': 'webm', 'url': TEST_URL}
,
249 info_dict
= _make_result(formats
)
252 ydl
= YDL({'format': '[format_id=abc-cba]'}
)
253 ydl
.process_ie_result(info_dict
.copy())
254 downloaded
= ydl
.downloaded_info_dicts
[0]
255 self
.assertEqual(downloaded
['format_id'], 'abc-cba')
257 # does not equal (!=)
258 ydl
= YDL({'format': '[format_id!=abc-cba]'}
)
259 ydl
.process_ie_result(info_dict
.copy())
260 downloaded
= ydl
.downloaded_info_dicts
[0]
261 self
.assertEqual(downloaded
['format_id'], 'zxc-cxz')
263 ydl
= YDL({'format': '[format_id!=abc-cba][format_id!=zxc-cxz]'}
)
264 self
.assertRaises(ExtractorError
, ydl
.process_ie_result
, info_dict
.copy())
267 ydl
= YDL({'format': '[format_id^=abc]'}
)
268 ydl
.process_ie_result(info_dict
.copy())
269 downloaded
= ydl
.downloaded_info_dicts
[0]
270 self
.assertEqual(downloaded
['format_id'], 'abc-cba')
272 # does not start with (!^=)
273 ydl
= YDL({'format': '[format_id!^=abc]'}
)
274 ydl
.process_ie_result(info_dict
.copy())
275 downloaded
= ydl
.downloaded_info_dicts
[0]
276 self
.assertEqual(downloaded
['format_id'], 'zxc-cxz')
278 ydl
= YDL({'format': '[format_id!^=abc][format_id!^=zxc]'}
)
279 self
.assertRaises(ExtractorError
, ydl
.process_ie_result
, info_dict
.copy())
282 ydl
= YDL({'format': '[format_id$=cba]'}
)
283 ydl
.process_ie_result(info_dict
.copy())
284 downloaded
= ydl
.downloaded_info_dicts
[0]
285 self
.assertEqual(downloaded
['format_id'], 'abc-cba')
287 # does not end with (!$=)
288 ydl
= YDL({'format': '[format_id!$=cba]'}
)
289 ydl
.process_ie_result(info_dict
.copy())
290 downloaded
= ydl
.downloaded_info_dicts
[0]
291 self
.assertEqual(downloaded
['format_id'], 'zxc-cxz')
293 ydl
= YDL({'format': '[format_id!$=cba][format_id!$=cxz]'}
)
294 self
.assertRaises(ExtractorError
, ydl
.process_ie_result
, info_dict
.copy())
297 ydl
= YDL({'format': '[format_id*=bc-cb]'}
)
298 ydl
.process_ie_result(info_dict
.copy())
299 downloaded
= ydl
.downloaded_info_dicts
[0]
300 self
.assertEqual(downloaded
['format_id'], 'abc-cba')
302 # does not contain (!*=)
303 ydl
= YDL({'format': '[format_id!*=bc-cb]'}
)
304 ydl
.process_ie_result(info_dict
.copy())
305 downloaded
= ydl
.downloaded_info_dicts
[0]
306 self
.assertEqual(downloaded
['format_id'], 'zxc-cxz')
308 ydl
= YDL({'format': '[format_id!*=abc][format_id!*=zxc]'}
)
309 self
.assertRaises(ExtractorError
, ydl
.process_ie_result
, info_dict
.copy())
311 ydl
= YDL({'format': '[format_id!*=-]'}
)
312 self
.assertRaises(ExtractorError
, ydl
.process_ie_result
, info_dict
.copy())
314 def test_youtube_format_selection(self
):
315 # FIXME: Rewrite in accordance with the new format sorting options
319 '38', '37', '46', '22', '45', '35', '44', '18', '34', '43', '6', '5', '17', '36', '13',
320 # Apple HTTP Live Streaming
321 '96', '95', '94', '93', '92', '132', '151',
323 '85', '84', '102', '83', '101', '82', '100',
325 '137', '248', '136', '247', '135', '246',
326 '245', '244', '134', '243', '133', '242', '160',
328 '141', '172', '140', '171', '139',
331 def format_info(f_id
):
332 info
= YoutubeIE
._formats
[f_id
].copy()
334 # XXX: In real cases InfoExtractor._parse_mpd_formats() fills up 'acodec'
335 # and 'vcodec', while in tests such information is incomplete since
336 # commit a6c2c24479e5f4827ceb06f64d855329c0a6f593
337 # test_YoutubeDL.test_youtube_format_selection is broken without
339 if 'acodec' in info
and 'vcodec' not in info
:
340 info
['vcodec'] = 'none'
341 elif 'vcodec' in info
and 'acodec' not in info
:
342 info
['acodec'] = 'none'
344 info
['format_id'] = f_id
345 info
['url'] = 'url:' + f_id
347 formats_order
= [format_info(f_id
) for f_id
in order
]
349 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
350 ydl
= YDL({'format': 'bestvideo+bestaudio'}
)
352 yie
._sort
_formats
(info_dict
['formats'])
353 ydl
.process_ie_result(info_dict
)
354 downloaded
= ydl
.downloaded_info_dicts
[0]
355 self
.assertEqual(downloaded
['format_id'], '248+172')
356 self
.assertEqual(downloaded
['ext'], 'mp4')
358 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
359 ydl
= YDL({'format': 'bestvideo[height>=999999]+bestaudio/best'}
)
361 yie
._sort
_formats
(info_dict
['formats'])
362 ydl
.process_ie_result(info_dict
)
363 downloaded
= ydl
.downloaded_info_dicts
[0]
364 self
.assertEqual(downloaded
['format_id'], '38')
366 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
367 ydl
= YDL({'format': 'bestvideo/best,bestaudio'}
)
369 yie
._sort
_formats
(info_dict
['formats'])
370 ydl
.process_ie_result(info_dict
)
371 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
372 self
.assertEqual(downloaded_ids
, ['137', '141'])
374 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
375 ydl
= YDL({'format': '(bestvideo[ext=mp4],bestvideo[ext=webm])+bestaudio'}
)
377 yie
._sort
_formats
(info_dict
['formats'])
378 ydl
.process_ie_result(info_dict
)
379 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
380 self
.assertEqual(downloaded_ids
, ['137+141', '248+141'])
382 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
383 ydl
= YDL({'format': '(bestvideo[ext=mp4],bestvideo[ext=webm])[height<=720]+bestaudio'}
)
385 yie
._sort
_formats
(info_dict
['formats'])
386 ydl
.process_ie_result(info_dict
)
387 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
388 self
.assertEqual(downloaded_ids
, ['136+141', '247+141'])
390 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
391 ydl
= YDL({'format': '(bestvideo[ext=none]/bestvideo[ext=webm])+bestaudio'}
)
393 yie
._sort
_formats
(info_dict
['formats'])
394 ydl
.process_ie_result(info_dict
)
395 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
396 self
.assertEqual(downloaded_ids
, ['248+141'])
398 for f1
, f2
in zip(formats_order
, formats_order
[1:]):
399 info_dict
= _make_result([f1
, f2
], extractor
='youtube')
400 ydl
= YDL({'format': 'best/bestvideo'}
)
402 yie
._sort
_formats
(info_dict
['formats'])
403 ydl
.process_ie_result(info_dict
)
404 downloaded
= ydl
.downloaded_info_dicts
[0]
405 self
.assertEqual(downloaded
['format_id'], f1
['format_id'])
407 info_dict
= _make_result([f2
, f1
], extractor
='youtube')
408 ydl
= YDL({'format': 'best/bestvideo'}
)
410 yie
._sort
_formats
(info_dict
['formats'])
411 ydl
.process_ie_result(info_dict
)
412 downloaded
= ydl
.downloaded_info_dicts
[0]
413 self
.assertEqual(downloaded
['format_id'], f1
['format_id'])
415 def test_audio_only_extractor_format_selection(self
):
416 # For extractors with incomplete formats (all formats are audio-only or
417 # video-only) best and worst should fallback to corresponding best/worst
418 # video-only or audio-only formats (as per
419 # https://github.com/ytdl-org/youtube-dl/pull/5556)
421 {'format_id': 'low', 'ext': 'mp3', 'preference': 1, 'vcodec': 'none', 'url': TEST_URL}
,
422 {'format_id': 'high', 'ext': 'mp3', 'preference': 2, 'vcodec': 'none', 'url': TEST_URL}
,
424 info_dict
= _make_result(formats
)
426 ydl
= YDL({'format': 'best'}
)
427 ydl
.process_ie_result(info_dict
.copy())
428 downloaded
= ydl
.downloaded_info_dicts
[0]
429 self
.assertEqual(downloaded
['format_id'], 'high')
431 ydl
= YDL({'format': 'worst'}
)
432 ydl
.process_ie_result(info_dict
.copy())
433 downloaded
= ydl
.downloaded_info_dicts
[0]
434 self
.assertEqual(downloaded
['format_id'], 'low')
436 def test_format_not_available(self
):
438 {'format_id': 'regular', 'ext': 'mp4', 'height': 360, 'url': TEST_URL}
,
439 {'format_id': 'video', 'ext': 'mp4', 'height': 720, 'acodec': 'none', 'url': TEST_URL}
,
441 info_dict
= _make_result(formats
)
443 # This must fail since complete video-audio format does not match filter
444 # and extractor does not provide incomplete only formats (i.e. only
445 # video-only or audio-only).
446 ydl
= YDL({'format': 'best[height>360]'}
)
447 self
.assertRaises(ExtractorError
, ydl
.process_ie_result
, info_dict
.copy())
449 def test_format_selection_issue_10083(self
):
450 # See https://github.com/ytdl-org/youtube-dl/issues/10083
452 {'format_id': 'regular', 'height': 360, 'url': TEST_URL}
,
453 {'format_id': 'video', 'height': 720, 'acodec': 'none', 'url': TEST_URL}
,
454 {'format_id': 'audio', 'vcodec': 'none', 'url': TEST_URL}
,
456 info_dict
= _make_result(formats
)
458 ydl
= YDL({'format': 'best[height>360]/bestvideo[height>360]+bestaudio'}
)
459 ydl
.process_ie_result(info_dict
.copy())
460 self
.assertEqual(ydl
.downloaded_info_dicts
[0]['format_id'], 'video+audio')
462 def test_invalid_format_specs(self
):
463 def assert_syntax_error(format_spec
):
464 ydl
= YDL({'format': format_spec}
)
465 info_dict
= _make_result([{'format_id': 'foo', 'url': TEST_URL}
])
466 self
.assertRaises(SyntaxError, ydl
.process_ie_result
, info_dict
)
468 assert_syntax_error('bestvideo,,best')
469 assert_syntax_error('+bestaudio')
470 assert_syntax_error('bestvideo+')
471 assert_syntax_error('/')
473 def test_format_filtering(self
):
475 {'format_id': 'A', 'filesize': 500, 'width': 1000}
,
476 {'format_id': 'B', 'filesize': 1000, 'width': 500}
,
477 {'format_id': 'C', 'filesize': 1000, 'width': 400}
,
478 {'format_id': 'D', 'filesize': 2000, 'width': 600}
,
479 {'format_id': 'E', 'filesize': 3000}
,
481 {'format_id': 'G', 'filesize': 1000000}
,
484 f
['url'] = 'http://_/'
486 info_dict
= _make_result(formats
)
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'], 'D')
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'], 'E')
498 ydl
= YDL({'format': 'best[filesize <= ? 3000]'}
)
499 ydl
.process_ie_result(info_dict
)
500 downloaded
= ydl
.downloaded_info_dicts
[0]
501 self
.assertEqual(downloaded
['format_id'], 'F')
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'], 'B')
508 ydl
= YDL({'format': 'best [filesize = 1000] [width!=450]'}
)
509 ydl
.process_ie_result(info_dict
)
510 downloaded
= ydl
.downloaded_info_dicts
[0]
511 self
.assertEqual(downloaded
['format_id'], 'C')
513 ydl
= YDL({'format': '[filesize>?1]'}
)
514 ydl
.process_ie_result(info_dict
)
515 downloaded
= ydl
.downloaded_info_dicts
[0]
516 self
.assertEqual(downloaded
['format_id'], 'G')
518 ydl
= YDL({'format': '[filesize<1M]'}
)
519 ydl
.process_ie_result(info_dict
)
520 downloaded
= ydl
.downloaded_info_dicts
[0]
521 self
.assertEqual(downloaded
['format_id'], 'E')
523 ydl
= YDL({'format': '[filesize<1MiB]'}
)
524 ydl
.process_ie_result(info_dict
)
525 downloaded
= ydl
.downloaded_info_dicts
[0]
526 self
.assertEqual(downloaded
['format_id'], 'G')
528 ydl
= YDL({'format': 'all[width>=400][width<=600]'}
)
529 ydl
.process_ie_result(info_dict
)
530 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
531 self
.assertEqual(downloaded_ids
, ['B', 'C', 'D'])
533 ydl
= YDL({'format': 'best[height<40]'}
)
535 ydl
.process_ie_result(info_dict
)
536 except ExtractorError
:
538 self
.assertEqual(ydl
.downloaded_info_dicts
, [])
540 def test_default_format_spec(self
):
541 ydl
= YDL({'simulate': True}
)
542 self
.assertEqual(ydl
._default
_format
_spec
({}), 'bestvideo*+bestaudio/best')
545 self
.assertEqual(ydl
._default
_format
_spec
({'is_live': True}
), 'best/bestvideo+bestaudio')
547 ydl
= YDL({'simulate': True}
)
548 self
.assertEqual(ydl
._default
_format
_spec
({'is_live': True}
), 'bestvideo*+bestaudio/best')
550 ydl
= YDL({'outtmpl': '-'}
)
551 self
.assertEqual(ydl
._default
_format
_spec
({}), 'best/bestvideo+bestaudio')
554 self
.assertEqual(ydl
._default
_format
_spec
({}, download
=False), 'bestvideo*+bestaudio/best')
555 self
.assertEqual(ydl
._default
_format
_spec
({'is_live': True}
), 'best/bestvideo+bestaudio')
558 class TestYoutubeDL(unittest
.TestCase
):
559 def test_subtitles(self
):
560 def s_formats(lang
, autocaption
=False):
563 'url': 'http://localhost/video.%s.%s' % (lang
, ext
),
564 '_auto': autocaption
,
565 } for ext
in ['vtt', 'srt', 'ass']]
566 subtitles
= dict((l
, s_formats(l
)) for l
in ['en', 'fr', 'es'])
567 auto_captions
= dict((l
, s_formats(l
, True)) for l
in ['it', 'pt', 'es'])
571 'url': 'http://localhost/video.mp4',
572 'subtitles': subtitles
,
573 'automatic_captions': auto_captions
,
575 'webpage_url': 'http://example.com/watch?v=shenanigans',
578 def get_info(params
={}):
579 params
.setdefault('simulate', True)
581 ydl
.report_warning
= lambda *args
, **kargs
: None
582 return ydl
.process_video_result(info_dict
, download
=False)
585 self
.assertFalse(result
.get('requested_subtitles'))
586 self
.assertEqual(result
['subtitles'], subtitles
)
587 self
.assertEqual(result
['automatic_captions'], auto_captions
)
589 result
= get_info({'writesubtitles': True}
)
590 subs
= result
['requested_subtitles']
591 self
.assertTrue(subs
)
592 self
.assertEqual(set(subs
.keys()), set(['en']))
593 self
.assertTrue(subs
['en'].get('data') is None)
594 self
.assertEqual(subs
['en']['ext'], 'ass')
596 result
= get_info({'writesubtitles': True, 'subtitlesformat': 'foo/srt'}
)
597 subs
= result
['requested_subtitles']
598 self
.assertEqual(subs
['en']['ext'], 'srt')
600 result
= get_info({'writesubtitles': True, 'subtitleslangs': ['es', 'fr', 'it']}
)
601 subs
= result
['requested_subtitles']
602 self
.assertTrue(subs
)
603 self
.assertEqual(set(subs
.keys()), set(['es', 'fr']))
605 result
= get_info({'writesubtitles': True, 'subtitleslangs': ['all', '-en']}
)
606 subs
= result
['requested_subtitles']
607 self
.assertTrue(subs
)
608 self
.assertEqual(set(subs
.keys()), set(['es', 'fr']))
610 result
= get_info({'writesubtitles': True, 'subtitleslangs': ['en', 'fr', '-en']}
)
611 subs
= result
['requested_subtitles']
612 self
.assertTrue(subs
)
613 self
.assertEqual(set(subs
.keys()), set(['fr']))
615 result
= get_info({'writesubtitles': True, 'subtitleslangs': ['-en', 'en']}
)
616 subs
= result
['requested_subtitles']
617 self
.assertTrue(subs
)
618 self
.assertEqual(set(subs
.keys()), set(['en']))
620 result
= get_info({'writesubtitles': True, 'subtitleslangs': ['e.+']}
)
621 subs
= result
['requested_subtitles']
622 self
.assertTrue(subs
)
623 self
.assertEqual(set(subs
.keys()), set(['es', 'en']))
625 result
= get_info({'writesubtitles': True, 'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']}
)
626 subs
= result
['requested_subtitles']
627 self
.assertTrue(subs
)
628 self
.assertEqual(set(subs
.keys()), set(['es', 'pt']))
629 self
.assertFalse(subs
['es']['_auto'])
630 self
.assertTrue(subs
['pt']['_auto'])
632 result
= get_info({'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']}
)
633 subs
= result
['requested_subtitles']
634 self
.assertTrue(subs
)
635 self
.assertEqual(set(subs
.keys()), set(['es', 'pt']))
636 self
.assertTrue(subs
['es']['_auto'])
637 self
.assertTrue(subs
['pt']['_auto'])
639 def test_add_extra_info(self
):
645 'playlist': 'funny videos',
647 YDL
.add_extra_info(test_dict
, extra_info
)
648 self
.assertEqual(test_dict
['extractor'], 'Foo')
649 self
.assertEqual(test_dict
['playlist'], 'funny videos')
658 'timestamp': 1618488000,
661 '_last_playlist_index': 100,
663 'formats': [{'id': 'id1'}
, {'id': 'id2'}
, {'id': 'id3'}
]
666 def test_prepare_outtmpl(self
):
667 def out(tmpl
, **params
):
668 params
['outtmpl'] = tmpl
669 ydl
= YoutubeDL(params
)
670 ydl
._num
_downloads
= 1
671 outtmpl
, tmpl_dict
= ydl
.prepare_outtmpl(tmpl
, self
.outtmpl_info
)
672 return outtmpl
% tmpl_dict
674 self
.assertEqual(out('%(id)s.%(ext)s'), '1234.mp4')
675 self
.assertEqual(out('%(duration_string)s'), '27:46:40')
676 self
.assertTrue(float_or_none(out('%(epoch)d')))
677 self
.assertEqual(out('%(resolution)s'), '1080p')
678 self
.assertEqual(out('%(playlist_index)s'), '001')
679 self
.assertEqual(out('%(autonumber)s'), '00001')
680 self
.assertEqual(out('%(autonumber+2)03d', autonumber_start
=3), '005')
681 self
.assertEqual(out('%(autonumber)s', autonumber_size
=3), '001')
683 self
.assertEqual(out('%%'), '%')
684 self
.assertEqual(out('%%%%'), '%%')
685 self
.assertEqual(out('%(invalid@tmpl|def)s', outtmpl_na_placeholder
='none'), 'none')
686 self
.assertEqual(out('%()s'), 'NA')
687 self
.assertEqual(out('%s'), '%s')
689 NA_TEST_OUTTMPL
= '%(uploader_date)s-%(width)d-%(x|def)s-%(id)s.%(ext)s'
690 self
.assertEqual(out(NA_TEST_OUTTMPL
), 'NA-NA-def-1234.mp4')
691 self
.assertEqual(out(NA_TEST_OUTTMPL
, outtmpl_na_placeholder
='none'), 'none-none-def-1234.mp4')
692 self
.assertEqual(out(NA_TEST_OUTTMPL
, outtmpl_na_placeholder
=''), '--def-1234.mp4')
694 FMT_TEST_OUTTMPL
= '%%(height)%s.%%(ext)s'
695 self
.assertEqual(out(FMT_TEST_OUTTMPL
% 's'), '1080.mp4')
696 self
.assertEqual(out(FMT_TEST_OUTTMPL
% 'd'), '1080.mp4')
697 self
.assertEqual(out(FMT_TEST_OUTTMPL
% '6d'), ' 1080.mp4')
698 self
.assertEqual(out(FMT_TEST_OUTTMPL
% '-6d'), '1080 .mp4')
699 self
.assertEqual(out(FMT_TEST_OUTTMPL
% '06d'), '001080.mp4')
700 self
.assertEqual(out(FMT_TEST_OUTTMPL
% ' 06d'), ' 01080.mp4')
701 self
.assertEqual(out(FMT_TEST_OUTTMPL
% ' 06d'), ' 01080.mp4')
702 self
.assertEqual(out(FMT_TEST_OUTTMPL
% '0 6d'), ' 01080.mp4')
703 self
.assertEqual(out(FMT_TEST_OUTTMPL
% '0 6d'), ' 01080.mp4')
704 self
.assertEqual(out(FMT_TEST_OUTTMPL
% ' 0 6d'), ' 01080.mp4')
706 self
.assertEqual(out('%(id)d'), '1234')
707 self
.assertEqual(out('%(id)d %(id)r'), "1234 '1234'")
708 self
.assertEqual(out('%(ext)s-%(ext|def)d'), 'mp4-def')
709 self
.assertEqual(out('%(width|0)04d'), '0000')
710 self
.assertEqual(out('%(width|)d', outtmpl_na_placeholder
='none'), '')
712 FORMATS
= self
.outtmpl_info
['formats']
713 self
.assertEqual(out('%(timestamp+-1000>%H-%M-%S)s'), '11-43-20')
714 self
.assertEqual(out('%(id+1-height+3)05d'), '00158')
715 self
.assertEqual(out('%(width+100)05d'), 'NA')
716 self
.assertEqual(out('%(formats.0)s'), str(FORMATS
[0]))
717 self
.assertEqual(out('%(formats.-1.id)s'), str(FORMATS
[-1]['id']))
718 self
.assertEqual(out('%(formats.3)s'), 'NA')
719 self
.assertEqual(out('%(formats.:2:-1)r'), repr(FORMATS
[:2:-1]))
720 self
.assertEqual(out('%(formats.0.id.-1+id)f'), '1235.000000')
722 def test_prepare_filename(self
):
724 params
= {'outtmpl': templ}
725 ydl
= YoutubeDL(params
)
726 return ydl
.prepare_filename(self
.outtmpl_info
)
728 self
.assertEqual(fname('%%'), '%')
729 self
.assertEqual(fname('%%%%'), '%%')
730 self
.assertEqual(fname('%%(width)06d.%(ext)s'), '%(width)06d.mp4')
731 self
.assertEqual(fname('%(width)06d.%(ext)s'), 'NA.mp4')
732 self
.assertEqual(fname('%(width)06d.%%(ext)s'), 'NA.%(ext)s')
733 self
.assertEqual(fname('%%(width)06d.%(ext)s'), '%(width)06d.mp4')
735 self
.assertEqual(fname('Hello %(title1)s'), 'Hello $PATH')
736 self
.assertEqual(fname('Hello %(title2)s'), 'Hello %PATH%')
738 self
.assertEqual(fname('%(id)r %(height)r'), "'1234' 1080")
739 self
.assertEqual(fname('%(formats.0)r'), "{'id' - 'id1'}")
741 def test_format_note(self
):
743 self
.assertEqual(ydl
._format
_note
({}), '')
744 assertRegexpMatches(self
, ydl
._format
_note
({
747 assertRegexpMatches(self
, ydl
._format
_note
({
751 def test_postprocessors(self
):
752 filename
= 'post-processor-testfile.mp4'
753 audiofile
= filename
+ '.mp3'
755 class SimplePP(PostProcessor
):
757 with open(audiofile
, 'wt') as f
:
759 return [info
['filepath']], info
761 def run_pp(params
, PP
):
762 with open(filename
, 'wt') as f
:
764 ydl
= YoutubeDL(params
)
765 ydl
.add_post_processor(PP())
766 ydl
.post_process(filename
, {'filepath': filename}
)
768 run_pp({'keepvideo': True}
, SimplePP
)
769 self
.assertTrue(os
.path
.exists(filename
), '%s doesn\'t exist' % filename
)
770 self
.assertTrue(os
.path
.exists(audiofile
), '%s doesn\'t exist' % audiofile
)
774 run_pp({'keepvideo': False}
, SimplePP
)
775 self
.assertFalse(os
.path
.exists(filename
), '%s exists' % filename
)
776 self
.assertTrue(os
.path
.exists(audiofile
), '%s doesn\'t exist' % audiofile
)
779 class ModifierPP(PostProcessor
):
781 with open(info
['filepath'], 'wt') as f
:
785 run_pp({'keepvideo': False}
, ModifierPP
)
786 self
.assertTrue(os
.path
.exists(filename
), '%s doesn\'t exist' % filename
)
789 def test_match_filter(self
):
790 class FilterYDL(YDL
):
791 def __init__(self
, *args
, **kwargs
):
792 super(FilterYDL
, self
).__init
__(*args
, **kwargs
)
793 self
.params
['simulate'] = True
795 def process_info(self
, info_dict
):
796 super(YDL
, self
).process_info(info_dict
)
798 def _match_entry(self
, info_dict
, incomplete
=False):
799 res
= super(FilterYDL
, self
)._match
_entry
(info_dict
, incomplete
)
801 self
.downloaded_info_dicts
.append(info_dict
)
810 'filesize': 10 * 1024,
812 'uploader': "變態妍字幕版 太妍 тест",
813 'creator': "тест ' 123 ' тест--",
814 'webpage_url': 'http://example.com/watch?v=shenanigans',
822 'description': 'foo',
823 'filesize': 5 * 1024,
825 'uploader': "тест 123",
826 'webpage_url': 'http://example.com/watch?v=SHENANIGANS',
828 videos
= [first
, second
]
830 def get_videos(filter_
=None):
831 ydl
= FilterYDL({'match_filter': filter_}
)
833 ydl
.process_ie_result(v
, download
=True)
834 return [v
['id'] for v
in ydl
.downloaded_info_dicts
]
837 self
.assertEqual(res
, ['1', '2'])
843 return 'Video id is not 1'
845 self
.assertEqual(res
, ['1'])
847 f
= match_filter_func('duration < 30')
849 self
.assertEqual(res
, ['2'])
851 f
= match_filter_func('description = foo')
853 self
.assertEqual(res
, ['2'])
855 f
= match_filter_func('description =? foo')
857 self
.assertEqual(res
, ['1', '2'])
859 f
= match_filter_func('filesize > 5KiB')
861 self
.assertEqual(res
, ['1'])
863 f
= match_filter_func('playlist_id = 42')
865 self
.assertEqual(res
, ['1'])
867 f
= match_filter_func('uploader = "變態妍字幕版 太妍 тест"')
869 self
.assertEqual(res
, ['1'])
871 f
= match_filter_func('uploader != "變態妍字幕版 太妍 тест"')
873 self
.assertEqual(res
, ['2'])
875 f
= match_filter_func('creator = "тест \' 123 \' тест--"')
877 self
.assertEqual(res
, ['1'])
879 f
= match_filter_func("creator = 'тест \\' 123 \\' тест--'")
881 self
.assertEqual(res
, ['1'])
883 f
= match_filter_func(r
"creator = 'тест \' 123 \' тест--' & duration > 30")
885 self
.assertEqual(res
, [])
887 def test_playlist_items_selection(self
):
890 'title': compat_str(i
),
892 } for i
in range(1, 5)]
897 'extractor': 'test:playlist',
898 'extractor_key': 'test:playlist',
899 'webpage_url': 'http://example.com',
902 def get_downloaded_info_dicts(params
):
904 # make a deep copy because the dictionary and nested entries
906 ydl
.process_ie_result(copy
.deepcopy(playlist
))
907 return ydl
.downloaded_info_dicts
910 return [int(v
['id']) for v
in get_downloaded_info_dicts(params
)]
913 self
.assertEqual(result
, [1, 2, 3, 4])
915 result
= get_ids({'playlistend': 10}
)
916 self
.assertEqual(result
, [1, 2, 3, 4])
918 result
= get_ids({'playlistend': 2}
)
919 self
.assertEqual(result
, [1, 2])
921 result
= get_ids({'playliststart': 10}
)
922 self
.assertEqual(result
, [])
924 result
= get_ids({'playliststart': 2}
)
925 self
.assertEqual(result
, [2, 3, 4])
927 result
= get_ids({'playlist_items': '2-4'}
)
928 self
.assertEqual(result
, [2, 3, 4])
930 result
= get_ids({'playlist_items': '2,4'}
)
931 self
.assertEqual(result
, [2, 4])
933 result
= get_ids({'playlist_items': '10'}
)
934 self
.assertEqual(result
, [])
936 result
= get_ids({'playlist_items': '3-10'}
)
937 self
.assertEqual(result
, [3, 4])
939 result
= get_ids({'playlist_items': '2-4,3-4,3'}
)
940 self
.assertEqual(result
, [2, 3, 4])
942 # Tests for https://github.com/ytdl-org/youtube-dl/issues/10591
944 result
= get_downloaded_info_dicts({'playlist_items': '2-4,3-4,3'}
)
945 self
.assertEqual(result
[0]['playlist_index'], 2)
946 self
.assertEqual(result
[1]['playlist_index'], 3)
948 result
= get_downloaded_info_dicts({'playlist_items': '2-4,3-4,3'}
)
949 self
.assertEqual(result
[0]['playlist_index'], 2)
950 self
.assertEqual(result
[1]['playlist_index'], 3)
951 self
.assertEqual(result
[2]['playlist_index'], 4)
953 result
= get_downloaded_info_dicts({'playlist_items': '4,2'}
)
954 self
.assertEqual(result
[0]['playlist_index'], 4)
955 self
.assertEqual(result
[1]['playlist_index'], 2)
958 def test_urlopen_no_file_protocol(self
):
959 # see https://github.com/ytdl-org/youtube-dl/issues/8227
961 self
.assertRaises(compat_urllib_error
.URLError
, ydl
.urlopen
, 'file:///etc/passwd')
963 def test_do_not_override_ie_key_in_url_transparent(self
):
966 class Foo1IE(InfoExtractor
):
967 _VALID_URL
= r
'foo1:'
969 def _real_extract(self
, url
):
971 '_type': 'url_transparent',
974 'title': 'foo1 title',
978 class Foo2IE(InfoExtractor
):
979 _VALID_URL
= r
'foo2:'
981 def _real_extract(self
, url
):
988 class Foo3IE(InfoExtractor
):
989 _VALID_URL
= r
'foo3:'
991 def _real_extract(self
, url
):
992 return _make_result([{'url': TEST_URL}
], title
='foo3 title')
994 ydl
.add_info_extractor(Foo1IE(ydl
))
995 ydl
.add_info_extractor(Foo2IE(ydl
))
996 ydl
.add_info_extractor(Foo3IE(ydl
))
997 ydl
.extract_info('foo1:')
998 downloaded
= ydl
.downloaded_info_dicts
[0]
999 self
.assertEqual(downloaded
['url'], TEST_URL
)
1000 self
.assertEqual(downloaded
['title'], 'foo1 title')
1001 self
.assertEqual(downloaded
['id'], 'testid')
1002 self
.assertEqual(downloaded
['extractor'], 'testex')
1003 self
.assertEqual(downloaded
['extractor_key'], 'TestEx')
1005 # Test case for https://github.com/ytdl-org/youtube-dl/issues/27064
1006 def test_ignoreerrors_for_playlist_with_url_transparent_iterable_entries(self
):
1009 def __init__(self
, *args
, **kwargs
):
1010 super(_YDL
, self
).__init
__(*args
, **kwargs
)
1012 def trouble(self
, s
, tb
=None):
1017 'ignoreerrors': True,
1020 class VideoIE(InfoExtractor
):
1021 _VALID_URL
= r
'video:(?P<id>\d+)'
1023 def _real_extract(self
, url
):
1024 video_id
= self
._match
_id
(url
)
1026 'format_id': 'default',
1030 raise ExtractorError('foo')
1033 'format_id': 'extra',
1038 'title': 'Video %s' % video_id
,
1042 class PlaylistIE(InfoExtractor
):
1043 _VALID_URL
= r
'playlist:'
1047 video_id
= compat_str(n
)
1049 '_type': 'url_transparent',
1050 'ie_key': VideoIE
.ie_key(),
1052 'url': 'video:%s' % video_id
,
1053 'title': 'Video Transparent %s' % video_id
,
1056 def _real_extract(self
, url
):
1057 return self
.playlist_result(self
._entries
())
1059 ydl
.add_info_extractor(VideoIE(ydl
))
1060 ydl
.add_info_extractor(PlaylistIE(ydl
))
1061 info
= ydl
.extract_info('playlist:')
1062 entries
= info
['entries']
1063 self
.assertEqual(len(entries
), 3)
1064 self
.assertTrue(entries
[0] is None)
1065 self
.assertTrue(entries
[1] is None)
1066 self
.assertEqual(len(ydl
.downloaded_info_dicts
), 1)
1067 downloaded
= ydl
.downloaded_info_dicts
[0]
1068 self
.assertEqual(entries
[2], downloaded
)
1069 self
.assertEqual(downloaded
['url'], TEST_URL
)
1070 self
.assertEqual(downloaded
['title'], 'Video Transparent 2')
1071 self
.assertEqual(downloaded
['id'], '2')
1072 self
.assertEqual(downloaded
['extractor'], 'Video')
1073 self
.assertEqual(downloaded
['extractor_key'], 'Video')
1076 if __name__
== '__main__':