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
, match_filter_func
22 TEST_URL
= 'http://localhost/sample.mp4'
26 def __init__(self
, *args
, **kwargs
):
27 super(YDL
, self
).__init
__(*args
, **kwargs
)
28 self
.downloaded_info_dicts
= []
31 def process_info(self
, info_dict
):
32 self
.downloaded_info_dicts
.append(info_dict
)
34 def to_screen(self
, msg
):
38 def _make_result(formats
, **kwargs
):
42 'title': 'testttitle',
43 'extractor': 'testex',
44 'extractor_key': 'TestEx',
45 'webpage_url': 'http://example.com/watch?v=shenanigans',
51 class TestFormatSelection(unittest
.TestCase
):
52 def test_prefer_free_formats(self
):
53 # Same resolution => download webm
55 ydl
.params
['prefer_free_formats'] = True
57 {'ext': 'webm', 'height': 460, 'url': TEST_URL}
,
58 {'ext': 'mp4', 'height': 460, 'url': TEST_URL}
,
60 info_dict
= _make_result(formats
)
62 yie
._sort
_formats
(info_dict
['formats'])
63 ydl
.process_ie_result(info_dict
)
64 downloaded
= ydl
.downloaded_info_dicts
[0]
65 self
.assertEqual(downloaded
['ext'], 'webm')
67 # Different resolution => download best quality (mp4)
69 ydl
.params
['prefer_free_formats'] = True
71 {'ext': 'webm', 'height': 720, 'url': TEST_URL}
,
72 {'ext': 'mp4', 'height': 1080, 'url': TEST_URL}
,
74 info_dict
['formats'] = formats
76 yie
._sort
_formats
(info_dict
['formats'])
77 ydl
.process_ie_result(info_dict
)
78 downloaded
= ydl
.downloaded_info_dicts
[0]
79 self
.assertEqual(downloaded
['ext'], 'mp4')
81 # No prefer_free_formats => prefer mp4 and webm
83 ydl
.params
['prefer_free_formats'] = False
85 {'ext': 'webm', 'height': 720, 'url': TEST_URL}
,
86 {'ext': 'mp4', 'height': 720, 'url': TEST_URL}
,
87 {'ext': 'flv', 'height': 720, 'url': TEST_URL}
,
89 info_dict
['formats'] = formats
91 yie
._sort
_formats
(info_dict
['formats'])
92 ydl
.process_ie_result(info_dict
)
93 downloaded
= ydl
.downloaded_info_dicts
[0]
94 self
.assertEqual(downloaded
['ext'], 'mp4')
97 ydl
.params
['prefer_free_formats'] = False
99 {'ext': 'flv', 'height': 720, 'url': TEST_URL}
,
100 {'ext': 'webm', 'height': 720, 'url': TEST_URL}
,
102 info_dict
['formats'] = formats
104 yie
._sort
_formats
(info_dict
['formats'])
105 ydl
.process_ie_result(info_dict
)
106 downloaded
= ydl
.downloaded_info_dicts
[0]
107 self
.assertEqual(downloaded
['ext'], 'webm')
109 def test_format_selection(self
):
111 {'format_id': '35', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL}
,
112 {'format_id': 'example-with-dashes', 'ext': 'webm', 'preference': 1, 'url': TEST_URL}
,
113 {'format_id': '45', 'ext': 'webm', 'preference': 2, 'url': TEST_URL}
,
114 {'format_id': '47', 'ext': 'webm', 'preference': 3, 'url': TEST_URL}
,
115 {'format_id': '2', 'ext': 'flv', 'preference': 4, 'url': TEST_URL}
,
117 info_dict
= _make_result(formats
)
119 ydl
= YDL({'format': '20/47'}
)
120 ydl
.process_ie_result(info_dict
.copy())
121 downloaded
= ydl
.downloaded_info_dicts
[0]
122 self
.assertEqual(downloaded
['format_id'], '47')
124 ydl
= YDL({'format': '20/71/worst'}
)
125 ydl
.process_ie_result(info_dict
.copy())
126 downloaded
= ydl
.downloaded_info_dicts
[0]
127 self
.assertEqual(downloaded
['format_id'], '35')
130 ydl
.process_ie_result(info_dict
.copy())
131 downloaded
= ydl
.downloaded_info_dicts
[0]
132 self
.assertEqual(downloaded
['format_id'], '2')
134 ydl
= YDL({'format': 'webm/mp4'}
)
135 ydl
.process_ie_result(info_dict
.copy())
136 downloaded
= ydl
.downloaded_info_dicts
[0]
137 self
.assertEqual(downloaded
['format_id'], '47')
139 ydl
= YDL({'format': '3gp/40/mp4'}
)
140 ydl
.process_ie_result(info_dict
.copy())
141 downloaded
= ydl
.downloaded_info_dicts
[0]
142 self
.assertEqual(downloaded
['format_id'], '35')
144 ydl
= YDL({'format': 'example-with-dashes'}
)
145 ydl
.process_ie_result(info_dict
.copy())
146 downloaded
= ydl
.downloaded_info_dicts
[0]
147 self
.assertEqual(downloaded
['format_id'], 'example-with-dashes')
149 def test_format_selection_audio(self
):
151 {'format_id': 'audio-low', 'ext': 'webm', 'preference': 1, 'vcodec': 'none', 'url': TEST_URL}
,
152 {'format_id': 'audio-mid', 'ext': 'webm', 'preference': 2, 'vcodec': 'none', 'url': TEST_URL}
,
153 {'format_id': 'audio-high', 'ext': 'flv', 'preference': 3, 'vcodec': 'none', 'url': TEST_URL}
,
154 {'format_id': 'vid', 'ext': 'mp4', 'preference': 4, 'url': TEST_URL}
,
156 info_dict
= _make_result(formats
)
158 ydl
= YDL({'format': 'bestaudio'}
)
159 ydl
.process_ie_result(info_dict
.copy())
160 downloaded
= ydl
.downloaded_info_dicts
[0]
161 self
.assertEqual(downloaded
['format_id'], 'audio-high')
163 ydl
= YDL({'format': 'worstaudio'}
)
164 ydl
.process_ie_result(info_dict
.copy())
165 downloaded
= ydl
.downloaded_info_dicts
[0]
166 self
.assertEqual(downloaded
['format_id'], 'audio-low')
169 {'format_id': 'vid-low', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL}
,
170 {'format_id': 'vid-high', 'ext': 'mp4', 'preference': 2, 'url': TEST_URL}
,
172 info_dict
= _make_result(formats
)
174 ydl
= YDL({'format': 'bestaudio/worstaudio/best'}
)
175 ydl
.process_ie_result(info_dict
.copy())
176 downloaded
= ydl
.downloaded_info_dicts
[0]
177 self
.assertEqual(downloaded
['format_id'], 'vid-high')
179 def test_format_selection_audio_exts(self
):
181 {'format_id': 'mp3-64', 'ext': 'mp3', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'}
,
182 {'format_id': 'ogg-64', 'ext': 'ogg', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'}
,
183 {'format_id': 'aac-64', 'ext': 'aac', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'}
,
184 {'format_id': 'mp3-32', 'ext': 'mp3', 'abr': 32, 'url': 'http://_', 'vcodec': 'none'}
,
185 {'format_id': 'aac-32', 'ext': 'aac', 'abr': 32, 'url': 'http://_', 'vcodec': 'none'}
,
188 info_dict
= _make_result(formats
)
189 ydl
= YDL({'format': 'best'}
)
191 ie
._sort
_formats
(info_dict
['formats'])
192 ydl
.process_ie_result(copy
.deepcopy(info_dict
))
193 downloaded
= ydl
.downloaded_info_dicts
[0]
194 self
.assertEqual(downloaded
['format_id'], 'aac-64')
196 ydl
= YDL({'format': 'mp3'}
)
198 ie
._sort
_formats
(info_dict
['formats'])
199 ydl
.process_ie_result(copy
.deepcopy(info_dict
))
200 downloaded
= ydl
.downloaded_info_dicts
[0]
201 self
.assertEqual(downloaded
['format_id'], 'mp3-64')
203 ydl
= YDL({'prefer_free_formats': True}
)
205 ie
._sort
_formats
(info_dict
['formats'])
206 ydl
.process_ie_result(copy
.deepcopy(info_dict
))
207 downloaded
= ydl
.downloaded_info_dicts
[0]
208 self
.assertEqual(downloaded
['format_id'], 'ogg-64')
210 def test_format_selection_video(self
):
212 {'format_id': 'dash-video-low', 'ext': 'mp4', 'preference': 1, 'acodec': 'none', 'url': TEST_URL}
,
213 {'format_id': 'dash-video-high', 'ext': 'mp4', 'preference': 2, 'acodec': 'none', 'url': TEST_URL}
,
214 {'format_id': 'vid', 'ext': 'mp4', 'preference': 3, 'url': TEST_URL}
,
216 info_dict
= _make_result(formats
)
218 ydl
= YDL({'format': 'bestvideo'}
)
219 ydl
.process_ie_result(info_dict
.copy())
220 downloaded
= ydl
.downloaded_info_dicts
[0]
221 self
.assertEqual(downloaded
['format_id'], 'dash-video-high')
223 ydl
= YDL({'format': 'worstvideo'}
)
224 ydl
.process_ie_result(info_dict
.copy())
225 downloaded
= ydl
.downloaded_info_dicts
[0]
226 self
.assertEqual(downloaded
['format_id'], 'dash-video-low')
228 ydl
= YDL({'format': 'bestvideo[format_id^=dash][format_id$=low]'}
)
229 ydl
.process_ie_result(info_dict
.copy())
230 downloaded
= ydl
.downloaded_info_dicts
[0]
231 self
.assertEqual(downloaded
['format_id'], 'dash-video-low')
234 {'format_id': 'vid-vcodec-dot', 'ext': 'mp4', 'preference': 1, 'vcodec': 'avc1.123456', 'acodec': 'none', 'url': TEST_URL}
,
236 info_dict
= _make_result(formats
)
238 ydl
= YDL({'format': 'bestvideo[vcodec=avc1.123456]'}
)
239 ydl
.process_ie_result(info_dict
.copy())
240 downloaded
= ydl
.downloaded_info_dicts
[0]
241 self
.assertEqual(downloaded
['format_id'], 'vid-vcodec-dot')
243 def test_format_selection_string_ops(self
):
245 {'format_id': 'abc-cba', 'ext': 'mp4', 'url': TEST_URL}
,
246 {'format_id': 'zxc-cxz', 'ext': 'webm', 'url': TEST_URL}
,
248 info_dict
= _make_result(formats
)
251 ydl
= YDL({'format': '[format_id=abc-cba]'}
)
252 ydl
.process_ie_result(info_dict
.copy())
253 downloaded
= ydl
.downloaded_info_dicts
[0]
254 self
.assertEqual(downloaded
['format_id'], 'abc-cba')
256 # does not equal (!=)
257 ydl
= YDL({'format': '[format_id!=abc-cba]'}
)
258 ydl
.process_ie_result(info_dict
.copy())
259 downloaded
= ydl
.downloaded_info_dicts
[0]
260 self
.assertEqual(downloaded
['format_id'], 'zxc-cxz')
262 ydl
= YDL({'format': '[format_id!=abc-cba][format_id!=zxc-cxz]'}
)
263 self
.assertRaises(ExtractorError
, ydl
.process_ie_result
, info_dict
.copy())
266 ydl
= YDL({'format': '[format_id^=abc]'}
)
267 ydl
.process_ie_result(info_dict
.copy())
268 downloaded
= ydl
.downloaded_info_dicts
[0]
269 self
.assertEqual(downloaded
['format_id'], 'abc-cba')
271 # does not start with (!^=)
272 ydl
= YDL({'format': '[format_id!^=abc]'}
)
273 ydl
.process_ie_result(info_dict
.copy())
274 downloaded
= ydl
.downloaded_info_dicts
[0]
275 self
.assertEqual(downloaded
['format_id'], 'zxc-cxz')
277 ydl
= YDL({'format': '[format_id!^=abc][format_id!^=zxc]'}
)
278 self
.assertRaises(ExtractorError
, ydl
.process_ie_result
, info_dict
.copy())
281 ydl
= YDL({'format': '[format_id$=cba]'}
)
282 ydl
.process_ie_result(info_dict
.copy())
283 downloaded
= ydl
.downloaded_info_dicts
[0]
284 self
.assertEqual(downloaded
['format_id'], 'abc-cba')
286 # does not end with (!$=)
287 ydl
= YDL({'format': '[format_id!$=cba]'}
)
288 ydl
.process_ie_result(info_dict
.copy())
289 downloaded
= ydl
.downloaded_info_dicts
[0]
290 self
.assertEqual(downloaded
['format_id'], 'zxc-cxz')
292 ydl
= YDL({'format': '[format_id!$=cba][format_id!$=cxz]'}
)
293 self
.assertRaises(ExtractorError
, ydl
.process_ie_result
, info_dict
.copy())
296 ydl
= YDL({'format': '[format_id*=bc-cb]'}
)
297 ydl
.process_ie_result(info_dict
.copy())
298 downloaded
= ydl
.downloaded_info_dicts
[0]
299 self
.assertEqual(downloaded
['format_id'], 'abc-cba')
301 # does not contain (!*=)
302 ydl
= YDL({'format': '[format_id!*=bc-cb]'}
)
303 ydl
.process_ie_result(info_dict
.copy())
304 downloaded
= ydl
.downloaded_info_dicts
[0]
305 self
.assertEqual(downloaded
['format_id'], 'zxc-cxz')
307 ydl
= YDL({'format': '[format_id!*=abc][format_id!*=zxc]'}
)
308 self
.assertRaises(ExtractorError
, ydl
.process_ie_result
, info_dict
.copy())
310 ydl
= YDL({'format': '[format_id!*=-]'}
)
311 self
.assertRaises(ExtractorError
, ydl
.process_ie_result
, info_dict
.copy())
313 def test_youtube_format_selection(self
):
314 # FIXME: Rewrite in accordance with the new format sorting options
318 '38', '37', '46', '22', '45', '35', '44', '18', '34', '43', '6', '5', '17', '36', '13',
319 # Apple HTTP Live Streaming
320 '96', '95', '94', '93', '92', '132', '151',
322 '85', '84', '102', '83', '101', '82', '100',
324 '137', '248', '136', '247', '135', '246',
325 '245', '244', '134', '243', '133', '242', '160',
327 '141', '172', '140', '171', '139',
330 def format_info(f_id
):
331 info
= YoutubeIE
._formats
[f_id
].copy()
333 # XXX: In real cases InfoExtractor._parse_mpd_formats() fills up 'acodec'
334 # and 'vcodec', while in tests such information is incomplete since
335 # commit a6c2c24479e5f4827ceb06f64d855329c0a6f593
336 # test_YoutubeDL.test_youtube_format_selection is broken without
338 if 'acodec' in info
and 'vcodec' not in info
:
339 info
['vcodec'] = 'none'
340 elif 'vcodec' in info
and 'acodec' not in info
:
341 info
['acodec'] = 'none'
343 info
['format_id'] = f_id
344 info
['url'] = 'url:' + f_id
346 formats_order
= [format_info(f_id
) for f_id
in order
]
348 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
349 ydl
= YDL({'format': 'bestvideo+bestaudio'}
)
351 yie
._sort
_formats
(info_dict
['formats'])
352 ydl
.process_ie_result(info_dict
)
353 downloaded
= ydl
.downloaded_info_dicts
[0]
354 self
.assertEqual(downloaded
['format_id'], '248+172')
355 self
.assertEqual(downloaded
['ext'], 'mp4')
357 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
358 ydl
= YDL({'format': 'bestvideo[height>=999999]+bestaudio/best'}
)
360 yie
._sort
_formats
(info_dict
['formats'])
361 ydl
.process_ie_result(info_dict
)
362 downloaded
= ydl
.downloaded_info_dicts
[0]
363 self
.assertEqual(downloaded
['format_id'], '38')
365 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
366 ydl
= YDL({'format': 'bestvideo/best,bestaudio'}
)
368 yie
._sort
_formats
(info_dict
['formats'])
369 ydl
.process_ie_result(info_dict
)
370 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
371 self
.assertEqual(downloaded_ids
, ['137', '141'])
373 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
374 ydl
= YDL({'format': '(bestvideo[ext=mp4],bestvideo[ext=webm])+bestaudio'}
)
376 yie
._sort
_formats
(info_dict
['formats'])
377 ydl
.process_ie_result(info_dict
)
378 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
379 self
.assertEqual(downloaded_ids
, ['137+141', '248+141'])
381 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
382 ydl
= YDL({'format': '(bestvideo[ext=mp4],bestvideo[ext=webm])[height<=720]+bestaudio'}
)
384 yie
._sort
_formats
(info_dict
['formats'])
385 ydl
.process_ie_result(info_dict
)
386 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
387 self
.assertEqual(downloaded_ids
, ['136+141', '247+141'])
389 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
390 ydl
= YDL({'format': '(bestvideo[ext=none]/bestvideo[ext=webm])+bestaudio'}
)
392 yie
._sort
_formats
(info_dict
['formats'])
393 ydl
.process_ie_result(info_dict
)
394 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
395 self
.assertEqual(downloaded_ids
, ['248+141'])
397 for f1
, f2
in zip(formats_order
, formats_order
[1:]):
398 info_dict
= _make_result([f1
, f2
], extractor
='youtube')
399 ydl
= YDL({'format': 'best/bestvideo'}
)
401 yie
._sort
_formats
(info_dict
['formats'])
402 ydl
.process_ie_result(info_dict
)
403 downloaded
= ydl
.downloaded_info_dicts
[0]
404 self
.assertEqual(downloaded
['format_id'], f1
['format_id'])
406 info_dict
= _make_result([f2
, f1
], extractor
='youtube')
407 ydl
= YDL({'format': 'best/bestvideo'}
)
409 yie
._sort
_formats
(info_dict
['formats'])
410 ydl
.process_ie_result(info_dict
)
411 downloaded
= ydl
.downloaded_info_dicts
[0]
412 self
.assertEqual(downloaded
['format_id'], f1
['format_id'])
414 def test_audio_only_extractor_format_selection(self
):
415 # For extractors with incomplete formats (all formats are audio-only or
416 # video-only) best and worst should fallback to corresponding best/worst
417 # video-only or audio-only formats (as per
418 # https://github.com/ytdl-org/youtube-dl/pull/5556)
420 {'format_id': 'low', 'ext': 'mp3', 'preference': 1, 'vcodec': 'none', 'url': TEST_URL}
,
421 {'format_id': 'high', 'ext': 'mp3', 'preference': 2, 'vcodec': 'none', 'url': TEST_URL}
,
423 info_dict
= _make_result(formats
)
425 ydl
= YDL({'format': 'best'}
)
426 ydl
.process_ie_result(info_dict
.copy())
427 downloaded
= ydl
.downloaded_info_dicts
[0]
428 self
.assertEqual(downloaded
['format_id'], 'high')
430 ydl
= YDL({'format': 'worst'}
)
431 ydl
.process_ie_result(info_dict
.copy())
432 downloaded
= ydl
.downloaded_info_dicts
[0]
433 self
.assertEqual(downloaded
['format_id'], 'low')
435 def test_format_not_available(self
):
437 {'format_id': 'regular', 'ext': 'mp4', 'height': 360, 'url': TEST_URL}
,
438 {'format_id': 'video', 'ext': 'mp4', 'height': 720, 'acodec': 'none', 'url': TEST_URL}
,
440 info_dict
= _make_result(formats
)
442 # This must fail since complete video-audio format does not match filter
443 # and extractor does not provide incomplete only formats (i.e. only
444 # video-only or audio-only).
445 ydl
= YDL({'format': 'best[height>360]'}
)
446 self
.assertRaises(ExtractorError
, ydl
.process_ie_result
, info_dict
.copy())
448 def test_format_selection_issue_10083(self
):
449 # See https://github.com/ytdl-org/youtube-dl/issues/10083
451 {'format_id': 'regular', 'height': 360, 'url': TEST_URL}
,
452 {'format_id': 'video', 'height': 720, 'acodec': 'none', 'url': TEST_URL}
,
453 {'format_id': 'audio', 'vcodec': 'none', 'url': TEST_URL}
,
455 info_dict
= _make_result(formats
)
457 ydl
= YDL({'format': 'best[height>360]/bestvideo[height>360]+bestaudio'}
)
458 ydl
.process_ie_result(info_dict
.copy())
459 self
.assertEqual(ydl
.downloaded_info_dicts
[0]['format_id'], 'video+audio')
461 def test_invalid_format_specs(self
):
462 def assert_syntax_error(format_spec
):
463 ydl
= YDL({'format': format_spec}
)
464 info_dict
= _make_result([{'format_id': 'foo', 'url': TEST_URL}
])
465 self
.assertRaises(SyntaxError, ydl
.process_ie_result
, info_dict
)
467 assert_syntax_error('bestvideo,,best')
468 assert_syntax_error('+bestaudio')
469 assert_syntax_error('bestvideo+')
470 assert_syntax_error('/')
472 def test_format_filtering(self
):
474 {'format_id': 'A', 'filesize': 500, 'width': 1000}
,
475 {'format_id': 'B', 'filesize': 1000, 'width': 500}
,
476 {'format_id': 'C', 'filesize': 1000, 'width': 400}
,
477 {'format_id': 'D', 'filesize': 2000, 'width': 600}
,
478 {'format_id': 'E', 'filesize': 3000}
,
480 {'format_id': 'G', 'filesize': 1000000}
,
483 f
['url'] = 'http://_/'
485 info_dict
= _make_result(formats
)
487 ydl
= YDL({'format': 'best[filesize<3000]'}
)
488 ydl
.process_ie_result(info_dict
)
489 downloaded
= ydl
.downloaded_info_dicts
[0]
490 self
.assertEqual(downloaded
['format_id'], 'D')
492 ydl
= YDL({'format': 'best[filesize<=3000]'}
)
493 ydl
.process_ie_result(info_dict
)
494 downloaded
= ydl
.downloaded_info_dicts
[0]
495 self
.assertEqual(downloaded
['format_id'], 'E')
497 ydl
= YDL({'format': 'best[filesize <= ? 3000]'}
)
498 ydl
.process_ie_result(info_dict
)
499 downloaded
= ydl
.downloaded_info_dicts
[0]
500 self
.assertEqual(downloaded
['format_id'], 'F')
502 ydl
= YDL({'format': 'best [filesize = 1000] [width>450]'}
)
503 ydl
.process_ie_result(info_dict
)
504 downloaded
= ydl
.downloaded_info_dicts
[0]
505 self
.assertEqual(downloaded
['format_id'], 'B')
507 ydl
= YDL({'format': 'best [filesize = 1000] [width!=450]'}
)
508 ydl
.process_ie_result(info_dict
)
509 downloaded
= ydl
.downloaded_info_dicts
[0]
510 self
.assertEqual(downloaded
['format_id'], 'C')
512 ydl
= YDL({'format': '[filesize>?1]'}
)
513 ydl
.process_ie_result(info_dict
)
514 downloaded
= ydl
.downloaded_info_dicts
[0]
515 self
.assertEqual(downloaded
['format_id'], 'G')
517 ydl
= YDL({'format': '[filesize<1M]'}
)
518 ydl
.process_ie_result(info_dict
)
519 downloaded
= ydl
.downloaded_info_dicts
[0]
520 self
.assertEqual(downloaded
['format_id'], 'E')
522 ydl
= YDL({'format': '[filesize<1MiB]'}
)
523 ydl
.process_ie_result(info_dict
)
524 downloaded
= ydl
.downloaded_info_dicts
[0]
525 self
.assertEqual(downloaded
['format_id'], 'G')
527 ydl
= YDL({'format': 'all[width>=400][width<=600]'}
)
528 ydl
.process_ie_result(info_dict
)
529 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
530 self
.assertEqual(downloaded_ids
, ['B', 'C', 'D'])
532 ydl
= YDL({'format': 'best[height<40]'}
)
534 ydl
.process_ie_result(info_dict
)
535 except ExtractorError
:
537 self
.assertEqual(ydl
.downloaded_info_dicts
, [])
539 def test_default_format_spec(self
):
540 ydl
= YDL({'simulate': True}
)
541 self
.assertEqual(ydl
._default
_format
_spec
({}), 'bestvideo*+bestaudio/best')
544 self
.assertEqual(ydl
._default
_format
_spec
({'is_live': True}
), 'best/bestvideo+bestaudio')
546 ydl
= YDL({'simulate': True}
)
547 self
.assertEqual(ydl
._default
_format
_spec
({'is_live': True}
), 'bestvideo*+bestaudio/best')
549 ydl
= YDL({'outtmpl': '-'}
)
550 self
.assertEqual(ydl
._default
_format
_spec
({}), 'best/bestvideo+bestaudio')
553 self
.assertEqual(ydl
._default
_format
_spec
({}, download
=False), 'bestvideo*+bestaudio/best')
554 self
.assertEqual(ydl
._default
_format
_spec
({'is_live': True}
), 'best/bestvideo+bestaudio')
557 class TestYoutubeDL(unittest
.TestCase
):
558 def test_subtitles(self
):
559 def s_formats(lang
, autocaption
=False):
562 'url': 'http://localhost/video.%s.%s' % (lang
, ext
),
563 '_auto': autocaption
,
564 } for ext
in ['vtt', 'srt', 'ass']]
565 subtitles
= dict((l
, s_formats(l
)) for l
in ['en', 'fr', 'es'])
566 auto_captions
= dict((l
, s_formats(l
, True)) for l
in ['it', 'pt', 'es'])
570 'url': 'http://localhost/video.mp4',
571 'subtitles': subtitles
,
572 'automatic_captions': auto_captions
,
574 'webpage_url': 'http://example.com/watch?v=shenanigans',
577 def get_info(params
={}):
578 params
.setdefault('simulate', True)
580 ydl
.report_warning
= lambda *args
, **kargs
: None
581 return ydl
.process_video_result(info_dict
, download
=False)
584 self
.assertFalse(result
.get('requested_subtitles'))
585 self
.assertEqual(result
['subtitles'], subtitles
)
586 self
.assertEqual(result
['automatic_captions'], auto_captions
)
588 result
= get_info({'writesubtitles': True}
)
589 subs
= result
['requested_subtitles']
590 self
.assertTrue(subs
)
591 self
.assertEqual(set(subs
.keys()), set(['en']))
592 self
.assertTrue(subs
['en'].get('data') is None)
593 self
.assertEqual(subs
['en']['ext'], 'ass')
595 result
= get_info({'writesubtitles': True, 'subtitlesformat': 'foo/srt'}
)
596 subs
= result
['requested_subtitles']
597 self
.assertEqual(subs
['en']['ext'], 'srt')
599 result
= get_info({'writesubtitles': True, 'subtitleslangs': ['es', 'fr', 'it']}
)
600 subs
= result
['requested_subtitles']
601 self
.assertTrue(subs
)
602 self
.assertEqual(set(subs
.keys()), set(['es', 'fr']))
604 result
= get_info({'writesubtitles': True, 'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']}
)
605 subs
= result
['requested_subtitles']
606 self
.assertTrue(subs
)
607 self
.assertEqual(set(subs
.keys()), set(['es', 'pt']))
608 self
.assertFalse(subs
['es']['_auto'])
609 self
.assertTrue(subs
['pt']['_auto'])
611 result
= get_info({'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']}
)
612 subs
= result
['requested_subtitles']
613 self
.assertTrue(subs
)
614 self
.assertEqual(set(subs
.keys()), set(['es', 'pt']))
615 self
.assertTrue(subs
['es']['_auto'])
616 self
.assertTrue(subs
['pt']['_auto'])
618 def test_add_extra_info(self
):
624 'playlist': 'funny videos',
626 YDL
.add_extra_info(test_dict
, extra_info
)
627 self
.assertEqual(test_dict
['extractor'], 'Foo')
628 self
.assertEqual(test_dict
['playlist'], 'funny videos')
630 def test_prepare_filename(self
):
640 def fname(templ
, na_placeholder
='NA'):
641 params
= {'outtmpl': templ}
642 if na_placeholder
!= 'NA':
643 params
['outtmpl_na_placeholder'] = na_placeholder
644 ydl
= YoutubeDL(params
)
645 return ydl
.prepare_filename(info
)
646 self
.assertEqual(fname('%(id)s.%(ext)s'), '1234.mp4')
647 self
.assertEqual(fname('%(id)s-%(width)s.%(ext)s'), '1234-NA.mp4')
648 NA_TEST_OUTTMPL
= '%(uploader_date)s-%(width)d-%(id)s.%(ext)s'
649 # Replace missing fields with 'NA' by default
650 self
.assertEqual(fname(NA_TEST_OUTTMPL
), 'NA-NA-1234.mp4')
651 # Or by provided placeholder
652 self
.assertEqual(fname(NA_TEST_OUTTMPL
, na_placeholder
='none'), 'none-none-1234.mp4')
653 self
.assertEqual(fname(NA_TEST_OUTTMPL
, na_placeholder
=''), '--1234.mp4')
654 self
.assertEqual(fname('%(height)d.%(ext)s'), '1080.mp4')
655 self
.assertEqual(fname('%(height)6d.%(ext)s'), ' 1080.mp4')
656 self
.assertEqual(fname('%(height)-6d.%(ext)s'), '1080 .mp4')
657 self
.assertEqual(fname('%(height)06d.%(ext)s'), '001080.mp4')
658 self
.assertEqual(fname('%(height) 06d.%(ext)s'), ' 01080.mp4')
659 self
.assertEqual(fname('%(height) 06d.%(ext)s'), ' 01080.mp4')
660 self
.assertEqual(fname('%(height)0 6d.%(ext)s'), ' 01080.mp4')
661 self
.assertEqual(fname('%(height)0 6d.%(ext)s'), ' 01080.mp4')
662 self
.assertEqual(fname('%(height) 0 6d.%(ext)s'), ' 01080.mp4')
663 self
.assertEqual(fname('%%'), '%')
664 self
.assertEqual(fname('%%%%'), '%%')
665 self
.assertEqual(fname('%%(height)06d.%(ext)s'), '%(height)06d.mp4')
666 self
.assertEqual(fname('%(width)06d.%(ext)s'), 'NA.mp4')
667 self
.assertEqual(fname('%(width)06d.%%(ext)s'), 'NA.%(ext)s')
668 self
.assertEqual(fname('%%(width)06d.%(ext)s'), '%(width)06d.mp4')
669 self
.assertEqual(fname('Hello %(title1)s'), 'Hello $PATH')
670 self
.assertEqual(fname('Hello %(title2)s'), 'Hello %PATH%')
672 def test_format_note(self
):
674 self
.assertEqual(ydl
._format
_note
({}), '')
675 assertRegexpMatches(self
, ydl
._format
_note
({
678 assertRegexpMatches(self
, ydl
._format
_note
({
682 def test_postprocessors(self
):
683 filename
= 'post-processor-testfile.mp4'
684 audiofile
= filename
+ '.mp3'
686 class SimplePP(PostProcessor
):
688 with open(audiofile
, 'wt') as f
:
690 return [info
['filepath']], info
692 def run_pp(params
, PP
):
693 with open(filename
, 'wt') as f
:
695 ydl
= YoutubeDL(params
)
696 ydl
.add_post_processor(PP())
697 ydl
.post_process(filename
, {'filepath': filename}
)
699 run_pp({'keepvideo': True}
, SimplePP
)
700 self
.assertTrue(os
.path
.exists(filename
), '%s doesn\'t exist' % filename
)
701 self
.assertTrue(os
.path
.exists(audiofile
), '%s doesn\'t exist' % audiofile
)
705 run_pp({'keepvideo': False}
, SimplePP
)
706 self
.assertFalse(os
.path
.exists(filename
), '%s exists' % filename
)
707 self
.assertTrue(os
.path
.exists(audiofile
), '%s doesn\'t exist' % audiofile
)
710 class ModifierPP(PostProcessor
):
712 with open(info
['filepath'], 'wt') as f
:
716 run_pp({'keepvideo': False}
, ModifierPP
)
717 self
.assertTrue(os
.path
.exists(filename
), '%s doesn\'t exist' % filename
)
720 def test_match_filter(self
):
721 class FilterYDL(YDL
):
722 def __init__(self
, *args
, **kwargs
):
723 super(FilterYDL
, self
).__init
__(*args
, **kwargs
)
724 self
.params
['simulate'] = True
726 def process_info(self
, info_dict
):
727 super(YDL
, self
).process_info(info_dict
)
729 def _match_entry(self
, info_dict
, incomplete
):
730 res
= super(FilterYDL
, self
)._match
_entry
(info_dict
, incomplete
)
732 self
.downloaded_info_dicts
.append(info_dict
)
741 'filesize': 10 * 1024,
743 'uploader': "變態妍字幕版 太妍 тест",
744 'creator': "тест ' 123 ' тест--",
745 'webpage_url': 'http://example.com/watch?v=shenanigans',
753 'description': 'foo',
754 'filesize': 5 * 1024,
756 'uploader': "тест 123",
757 'webpage_url': 'http://example.com/watch?v=SHENANIGANS',
759 videos
= [first
, second
]
761 def get_videos(filter_
=None):
762 ydl
= FilterYDL({'match_filter': filter_}
)
764 ydl
.process_ie_result(v
, download
=True)
765 return [v
['id'] for v
in ydl
.downloaded_info_dicts
]
768 self
.assertEqual(res
, ['1', '2'])
774 return 'Video id is not 1'
776 self
.assertEqual(res
, ['1'])
778 f
= match_filter_func('duration < 30')
780 self
.assertEqual(res
, ['2'])
782 f
= match_filter_func('description = foo')
784 self
.assertEqual(res
, ['2'])
786 f
= match_filter_func('description =? foo')
788 self
.assertEqual(res
, ['1', '2'])
790 f
= match_filter_func('filesize > 5KiB')
792 self
.assertEqual(res
, ['1'])
794 f
= match_filter_func('playlist_id = 42')
796 self
.assertEqual(res
, ['1'])
798 f
= match_filter_func('uploader = "變態妍字幕版 太妍 тест"')
800 self
.assertEqual(res
, ['1'])
802 f
= match_filter_func('uploader != "變態妍字幕版 太妍 тест"')
804 self
.assertEqual(res
, ['2'])
806 f
= match_filter_func('creator = "тест \' 123 \' тест--"')
808 self
.assertEqual(res
, ['1'])
810 f
= match_filter_func("creator = 'тест \\' 123 \\' тест--'")
812 self
.assertEqual(res
, ['1'])
814 f
= match_filter_func(r
"creator = 'тест \' 123 \' тест--' & duration > 30")
816 self
.assertEqual(res
, [])
818 def test_playlist_items_selection(self
):
821 'title': compat_str(i
),
823 } for i
in range(1, 5)]
828 'extractor': 'test:playlist',
829 'extractor_key': 'test:playlist',
830 'webpage_url': 'http://example.com',
833 def get_downloaded_info_dicts(params
):
835 # make a deep copy because the dictionary and nested entries
837 ydl
.process_ie_result(copy
.deepcopy(playlist
))
838 return ydl
.downloaded_info_dicts
841 return [int(v
['id']) for v
in get_downloaded_info_dicts(params
)]
844 self
.assertEqual(result
, [1, 2, 3, 4])
846 result
= get_ids({'playlistend': 10}
)
847 self
.assertEqual(result
, [1, 2, 3, 4])
849 result
= get_ids({'playlistend': 2}
)
850 self
.assertEqual(result
, [1, 2])
852 result
= get_ids({'playliststart': 10}
)
853 self
.assertEqual(result
, [])
855 result
= get_ids({'playliststart': 2}
)
856 self
.assertEqual(result
, [2, 3, 4])
858 result
= get_ids({'playlist_items': '2-4'}
)
859 self
.assertEqual(result
, [2, 3, 4])
861 result
= get_ids({'playlist_items': '2,4'}
)
862 self
.assertEqual(result
, [2, 4])
864 result
= get_ids({'playlist_items': '10'}
)
865 self
.assertEqual(result
, [])
867 result
= get_ids({'playlist_items': '3-10'}
)
868 self
.assertEqual(result
, [3, 4])
870 result
= get_ids({'playlist_items': '2-4,3-4,3'}
)
871 self
.assertEqual(result
, [2, 3, 4])
873 # Tests for https://github.com/ytdl-org/youtube-dl/issues/10591
875 result
= get_downloaded_info_dicts({'playlist_items': '2-4,3-4,3'}
)
876 self
.assertEqual(result
[0]['playlist_index'], 2)
877 self
.assertEqual(result
[1]['playlist_index'], 3)
879 result
= get_downloaded_info_dicts({'playlist_items': '2-4,3-4,3'}
)
880 self
.assertEqual(result
[0]['playlist_index'], 2)
881 self
.assertEqual(result
[1]['playlist_index'], 3)
882 self
.assertEqual(result
[2]['playlist_index'], 4)
884 result
= get_downloaded_info_dicts({'playlist_items': '4,2'}
)
885 self
.assertEqual(result
[0]['playlist_index'], 4)
886 self
.assertEqual(result
[1]['playlist_index'], 2)
889 def test_urlopen_no_file_protocol(self
):
890 # see https://github.com/ytdl-org/youtube-dl/issues/8227
892 self
.assertRaises(compat_urllib_error
.URLError
, ydl
.urlopen
, 'file:///etc/passwd')
894 def test_do_not_override_ie_key_in_url_transparent(self
):
897 class Foo1IE(InfoExtractor
):
898 _VALID_URL
= r
'foo1:'
900 def _real_extract(self
, url
):
902 '_type': 'url_transparent',
905 'title': 'foo1 title',
909 class Foo2IE(InfoExtractor
):
910 _VALID_URL
= r
'foo2:'
912 def _real_extract(self
, url
):
919 class Foo3IE(InfoExtractor
):
920 _VALID_URL
= r
'foo3:'
922 def _real_extract(self
, url
):
923 return _make_result([{'url': TEST_URL}
], title
='foo3 title')
925 ydl
.add_info_extractor(Foo1IE(ydl
))
926 ydl
.add_info_extractor(Foo2IE(ydl
))
927 ydl
.add_info_extractor(Foo3IE(ydl
))
928 ydl
.extract_info('foo1:')
929 downloaded
= ydl
.downloaded_info_dicts
[0]
930 self
.assertEqual(downloaded
['url'], TEST_URL
)
931 self
.assertEqual(downloaded
['title'], 'foo1 title')
932 self
.assertEqual(downloaded
['id'], 'testid')
933 self
.assertEqual(downloaded
['extractor'], 'testex')
934 self
.assertEqual(downloaded
['extractor_key'], 'TestEx')
936 # Test case for https://github.com/ytdl-org/youtube-dl/issues/27064
937 def test_ignoreerrors_for_playlist_with_url_transparent_iterable_entries(self
):
940 def __init__(self
, *args
, **kwargs
):
941 super(_YDL
, self
).__init
__(*args
, **kwargs
)
943 def trouble(self
, s
, tb
=None):
948 'ignoreerrors': True,
951 class VideoIE(InfoExtractor
):
952 _VALID_URL
= r
'video:(?P<id>\d+)'
954 def _real_extract(self
, url
):
955 video_id
= self
._match
_id
(url
)
957 'format_id': 'default',
961 raise ExtractorError('foo')
964 'format_id': 'extra',
969 'title': 'Video %s' % video_id
,
973 class PlaylistIE(InfoExtractor
):
974 _VALID_URL
= r
'playlist:'
978 video_id
= compat_str(n
)
980 '_type': 'url_transparent',
981 'ie_key': VideoIE
.ie_key(),
983 'url': 'video:%s' % video_id
,
984 'title': 'Video Transparent %s' % video_id
,
987 def _real_extract(self
, url
):
988 return self
.playlist_result(self
._entries
())
990 ydl
.add_info_extractor(VideoIE(ydl
))
991 ydl
.add_info_extractor(PlaylistIE(ydl
))
992 info
= ydl
.extract_info('playlist:')
993 entries
= info
['entries']
994 self
.assertEqual(len(entries
), 3)
995 self
.assertTrue(entries
[0] is None)
996 self
.assertTrue(entries
[1] is None)
997 self
.assertEqual(len(ydl
.downloaded_info_dicts
), 1)
998 downloaded
= ydl
.downloaded_info_dicts
[0]
999 self
.assertEqual(entries
[2], downloaded
)
1000 self
.assertEqual(downloaded
['url'], TEST_URL
)
1001 self
.assertEqual(downloaded
['title'], 'Video Transparent 2')
1002 self
.assertEqual(downloaded
['id'], '2')
1003 self
.assertEqual(downloaded
['extractor'], 'Video')
1004 self
.assertEqual(downloaded
['extractor_key'], 'Video')
1007 if __name__
== '__main__':