4 from __future__
import unicode_literals
6 # Allow direct execution
10 sys
.path
.insert(0, os
.path
.dirname(os
.path
.dirname(os
.path
.abspath(__file__
))))
14 from test
.helper
import FakeYDL
, assertRegexpMatches
15 from youtube_dlc
import YoutubeDL
16 from youtube_dlc
.compat
import compat_str
, compat_urllib_error
17 from youtube_dlc
.extractor
import YoutubeIE
18 from youtube_dlc
.extractor
.common
import InfoExtractor
19 from youtube_dlc
.postprocessor
.common
import PostProcessor
20 from youtube_dlc
.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 flv for greater compatibility
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'], 'flv')
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
):
315 '38', '37', '46', '22', '45', '35', '44', '18', '34', '43', '6', '5', '17', '36', '13',
316 # Apple HTTP Live Streaming
317 '96', '95', '94', '93', '92', '132', '151',
319 '85', '84', '102', '83', '101', '82', '100',
321 '137', '248', '136', '247', '135', '246',
322 '245', '244', '134', '243', '133', '242', '160',
324 '141', '172', '140', '171', '139',
327 def format_info(f_id
):
328 info
= YoutubeIE
._formats
[f_id
].copy()
330 # XXX: In real cases InfoExtractor._parse_mpd_formats() fills up 'acodec'
331 # and 'vcodec', while in tests such information is incomplete since
332 # commit a6c2c24479e5f4827ceb06f64d855329c0a6f593
333 # test_YoutubeDL.test_youtube_format_selection is broken without
335 if 'acodec' in info
and 'vcodec' not in info
:
336 info
['vcodec'] = 'none'
337 elif 'vcodec' in info
and 'acodec' not in info
:
338 info
['acodec'] = 'none'
340 info
['format_id'] = f_id
341 info
['url'] = 'url:' + f_id
343 formats_order
= [format_info(f_id
) for f_id
in order
]
345 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
346 ydl
= YDL({'format': 'bestvideo+bestaudio'}
)
348 yie
._sort
_formats
(info_dict
['formats'])
349 ydl
.process_ie_result(info_dict
)
350 downloaded
= ydl
.downloaded_info_dicts
[0]
351 self
.assertEqual(downloaded
['format_id'], '137+141')
352 self
.assertEqual(downloaded
['ext'], 'mp4')
354 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
355 ydl
= YDL({'format': 'bestvideo[height>=999999]+bestaudio/best'}
)
357 yie
._sort
_formats
(info_dict
['formats'])
358 ydl
.process_ie_result(info_dict
)
359 downloaded
= ydl
.downloaded_info_dicts
[0]
360 self
.assertEqual(downloaded
['format_id'], '38')
362 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
363 ydl
= YDL({'format': 'bestvideo/best,bestaudio'}
)
365 yie
._sort
_formats
(info_dict
['formats'])
366 ydl
.process_ie_result(info_dict
)
367 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
368 self
.assertEqual(downloaded_ids
, ['137', '141'])
370 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
371 ydl
= YDL({'format': '(bestvideo[ext=mp4],bestvideo[ext=webm])+bestaudio'}
)
373 yie
._sort
_formats
(info_dict
['formats'])
374 ydl
.process_ie_result(info_dict
)
375 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
376 self
.assertEqual(downloaded_ids
, ['137+141', '248+141'])
378 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
379 ydl
= YDL({'format': '(bestvideo[ext=mp4],bestvideo[ext=webm])[height<=720]+bestaudio'}
)
381 yie
._sort
_formats
(info_dict
['formats'])
382 ydl
.process_ie_result(info_dict
)
383 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
384 self
.assertEqual(downloaded_ids
, ['136+141', '247+141'])
386 info_dict
= _make_result(list(formats_order
), extractor
='youtube')
387 ydl
= YDL({'format': '(bestvideo[ext=none]/bestvideo[ext=webm])+bestaudio'}
)
389 yie
._sort
_formats
(info_dict
['formats'])
390 ydl
.process_ie_result(info_dict
)
391 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
392 self
.assertEqual(downloaded_ids
, ['248+141'])
394 for f1
, f2
in zip(formats_order
, formats_order
[1:]):
395 info_dict
= _make_result([f1
, f2
], extractor
='youtube')
396 ydl
= YDL({'format': 'best/bestvideo'}
)
398 yie
._sort
_formats
(info_dict
['formats'])
399 ydl
.process_ie_result(info_dict
)
400 downloaded
= ydl
.downloaded_info_dicts
[0]
401 self
.assertEqual(downloaded
['format_id'], f1
['format_id'])
403 info_dict
= _make_result([f2
, f1
], extractor
='youtube')
404 ydl
= YDL({'format': 'best/bestvideo'}
)
406 yie
._sort
_formats
(info_dict
['formats'])
407 ydl
.process_ie_result(info_dict
)
408 downloaded
= ydl
.downloaded_info_dicts
[0]
409 self
.assertEqual(downloaded
['format_id'], f1
['format_id'])
411 def test_audio_only_extractor_format_selection(self
):
412 # For extractors with incomplete formats (all formats are audio-only or
413 # video-only) best and worst should fallback to corresponding best/worst
414 # video-only or audio-only formats (as per
415 # https://github.com/ytdl-org/youtube-dl/pull/5556)
417 {'format_id': 'low', 'ext': 'mp3', 'preference': 1, 'vcodec': 'none', 'url': TEST_URL}
,
418 {'format_id': 'high', 'ext': 'mp3', 'preference': 2, 'vcodec': 'none', 'url': TEST_URL}
,
420 info_dict
= _make_result(formats
)
422 ydl
= YDL({'format': 'best'}
)
423 ydl
.process_ie_result(info_dict
.copy())
424 downloaded
= ydl
.downloaded_info_dicts
[0]
425 self
.assertEqual(downloaded
['format_id'], 'high')
427 ydl
= YDL({'format': 'worst'}
)
428 ydl
.process_ie_result(info_dict
.copy())
429 downloaded
= ydl
.downloaded_info_dicts
[0]
430 self
.assertEqual(downloaded
['format_id'], 'low')
432 def test_format_not_available(self
):
434 {'format_id': 'regular', 'ext': 'mp4', 'height': 360, 'url': TEST_URL}
,
435 {'format_id': 'video', 'ext': 'mp4', 'height': 720, 'acodec': 'none', 'url': TEST_URL}
,
437 info_dict
= _make_result(formats
)
439 # This must fail since complete video-audio format does not match filter
440 # and extractor does not provide incomplete only formats (i.e. only
441 # video-only or audio-only).
442 ydl
= YDL({'format': 'best[height>360]'}
)
443 self
.assertRaises(ExtractorError
, ydl
.process_ie_result
, info_dict
.copy())
445 def test_format_selection_issue_10083(self
):
446 # See https://github.com/ytdl-org/youtube-dl/issues/10083
448 {'format_id': 'regular', 'height': 360, 'url': TEST_URL}
,
449 {'format_id': 'video', 'height': 720, 'acodec': 'none', 'url': TEST_URL}
,
450 {'format_id': 'audio', 'vcodec': 'none', 'url': TEST_URL}
,
452 info_dict
= _make_result(formats
)
454 ydl
= YDL({'format': 'best[height>360]/bestvideo[height>360]+bestaudio'}
)
455 ydl
.process_ie_result(info_dict
.copy())
456 self
.assertEqual(ydl
.downloaded_info_dicts
[0]['format_id'], 'video+audio')
458 def test_invalid_format_specs(self
):
459 def assert_syntax_error(format_spec
):
460 ydl
= YDL({'format': format_spec}
)
461 info_dict
= _make_result([{'format_id': 'foo', 'url': TEST_URL}
])
462 self
.assertRaises(SyntaxError, ydl
.process_ie_result
, info_dict
)
464 assert_syntax_error('bestvideo,,best')
465 assert_syntax_error('+bestaudio')
466 assert_syntax_error('bestvideo+')
467 assert_syntax_error('/')
469 def test_format_filtering(self
):
471 {'format_id': 'A', 'filesize': 500, 'width': 1000}
,
472 {'format_id': 'B', 'filesize': 1000, 'width': 500}
,
473 {'format_id': 'C', 'filesize': 1000, 'width': 400}
,
474 {'format_id': 'D', 'filesize': 2000, 'width': 600}
,
475 {'format_id': 'E', 'filesize': 3000}
,
477 {'format_id': 'G', 'filesize': 1000000}
,
480 f
['url'] = 'http://_/'
482 info_dict
= _make_result(formats
)
484 ydl
= YDL({'format': 'best[filesize<3000]'}
)
485 ydl
.process_ie_result(info_dict
)
486 downloaded
= ydl
.downloaded_info_dicts
[0]
487 self
.assertEqual(downloaded
['format_id'], 'D')
489 ydl
= YDL({'format': 'best[filesize<=3000]'}
)
490 ydl
.process_ie_result(info_dict
)
491 downloaded
= ydl
.downloaded_info_dicts
[0]
492 self
.assertEqual(downloaded
['format_id'], 'E')
494 ydl
= YDL({'format': 'best[filesize <= ? 3000]'}
)
495 ydl
.process_ie_result(info_dict
)
496 downloaded
= ydl
.downloaded_info_dicts
[0]
497 self
.assertEqual(downloaded
['format_id'], 'F')
499 ydl
= YDL({'format': 'best [filesize = 1000] [width>450]'}
)
500 ydl
.process_ie_result(info_dict
)
501 downloaded
= ydl
.downloaded_info_dicts
[0]
502 self
.assertEqual(downloaded
['format_id'], 'B')
504 ydl
= YDL({'format': 'best [filesize = 1000] [width!=450]'}
)
505 ydl
.process_ie_result(info_dict
)
506 downloaded
= ydl
.downloaded_info_dicts
[0]
507 self
.assertEqual(downloaded
['format_id'], 'C')
509 ydl
= YDL({'format': '[filesize>?1]'}
)
510 ydl
.process_ie_result(info_dict
)
511 downloaded
= ydl
.downloaded_info_dicts
[0]
512 self
.assertEqual(downloaded
['format_id'], 'G')
514 ydl
= YDL({'format': '[filesize<1M]'}
)
515 ydl
.process_ie_result(info_dict
)
516 downloaded
= ydl
.downloaded_info_dicts
[0]
517 self
.assertEqual(downloaded
['format_id'], 'E')
519 ydl
= YDL({'format': '[filesize<1MiB]'}
)
520 ydl
.process_ie_result(info_dict
)
521 downloaded
= ydl
.downloaded_info_dicts
[0]
522 self
.assertEqual(downloaded
['format_id'], 'G')
524 ydl
= YDL({'format': 'all[width>=400][width<=600]'}
)
525 ydl
.process_ie_result(info_dict
)
526 downloaded_ids
= [info
['format_id'] for info
in ydl
.downloaded_info_dicts
]
527 self
.assertEqual(downloaded_ids
, ['B', 'C', 'D'])
529 ydl
= YDL({'format': 'best[height<40]'}
)
531 ydl
.process_ie_result(info_dict
)
532 except ExtractorError
:
534 self
.assertEqual(ydl
.downloaded_info_dicts
, [])
536 def test_default_format_spec(self
):
537 ydl
= YDL({'simulate': True}
)
538 self
.assertEqual(ydl
._default
_format
_spec
({}), 'bestvideo+bestaudio/best')
541 self
.assertEqual(ydl
._default
_format
_spec
({'is_live': True}
), 'best/bestvideo+bestaudio')
543 ydl
= YDL({'simulate': True}
)
544 self
.assertEqual(ydl
._default
_format
_spec
({'is_live': True}
), 'bestvideo+bestaudio/best')
546 ydl
= YDL({'outtmpl': '-'}
)
547 self
.assertEqual(ydl
._default
_format
_spec
({}), 'best/bestvideo+bestaudio')
550 self
.assertEqual(ydl
._default
_format
_spec
({}, download
=False), 'bestvideo+bestaudio/best')
551 self
.assertEqual(ydl
._default
_format
_spec
({'is_live': True}
), 'best/bestvideo+bestaudio')
554 class TestYoutubeDL(unittest
.TestCase
):
555 def test_subtitles(self
):
556 def s_formats(lang
, autocaption
=False):
559 'url': 'http://localhost/video.%s.%s' % (lang
, ext
),
560 '_auto': autocaption
,
561 } for ext
in ['vtt', 'srt', 'ass']]
562 subtitles
= dict((l
, s_formats(l
)) for l
in ['en', 'fr', 'es'])
563 auto_captions
= dict((l
, s_formats(l
, True)) for l
in ['it', 'pt', 'es'])
567 'url': 'http://localhost/video.mp4',
568 'subtitles': subtitles
,
569 'automatic_captions': auto_captions
,
571 'webpage_url': 'http://example.com/watch?v=shenanigans',
574 def get_info(params
={}):
575 params
.setdefault('simulate', True)
577 ydl
.report_warning
= lambda *args
, **kargs
: None
578 return ydl
.process_video_result(info_dict
, download
=False)
581 self
.assertFalse(result
.get('requested_subtitles'))
582 self
.assertEqual(result
['subtitles'], subtitles
)
583 self
.assertEqual(result
['automatic_captions'], auto_captions
)
585 result
= get_info({'writesubtitles': True}
)
586 subs
= result
['requested_subtitles']
587 self
.assertTrue(subs
)
588 self
.assertEqual(set(subs
.keys()), set(['en']))
589 self
.assertTrue(subs
['en'].get('data') is None)
590 self
.assertEqual(subs
['en']['ext'], 'ass')
592 result
= get_info({'writesubtitles': True, 'subtitlesformat': 'foo/srt'}
)
593 subs
= result
['requested_subtitles']
594 self
.assertEqual(subs
['en']['ext'], 'srt')
596 result
= get_info({'writesubtitles': True, 'subtitleslangs': ['es', 'fr', 'it']}
)
597 subs
= result
['requested_subtitles']
598 self
.assertTrue(subs
)
599 self
.assertEqual(set(subs
.keys()), set(['es', 'fr']))
601 result
= get_info({'writesubtitles': True, 'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']}
)
602 subs
= result
['requested_subtitles']
603 self
.assertTrue(subs
)
604 self
.assertEqual(set(subs
.keys()), set(['es', 'pt']))
605 self
.assertFalse(subs
['es']['_auto'])
606 self
.assertTrue(subs
['pt']['_auto'])
608 result
= get_info({'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']}
)
609 subs
= result
['requested_subtitles']
610 self
.assertTrue(subs
)
611 self
.assertEqual(set(subs
.keys()), set(['es', 'pt']))
612 self
.assertTrue(subs
['es']['_auto'])
613 self
.assertTrue(subs
['pt']['_auto'])
615 def test_add_extra_info(self
):
621 'playlist': 'funny videos',
623 YDL
.add_extra_info(test_dict
, extra_info
)
624 self
.assertEqual(test_dict
['extractor'], 'Foo')
625 self
.assertEqual(test_dict
['playlist'], 'funny videos')
627 def test_prepare_filename(self
):
638 ydl
= YoutubeDL({'outtmpl': templ}
)
639 return ydl
.prepare_filename(info
)
640 self
.assertEqual(fname('%(id)s.%(ext)s'), '1234.mp4')
641 self
.assertEqual(fname('%(id)s-%(width)s.%(ext)s'), '1234-NA.mp4')
642 # Replace missing fields with 'NA'
643 self
.assertEqual(fname('%(uploader_date)s-%(id)s.%(ext)s'), 'NA-1234.mp4')
644 self
.assertEqual(fname('%(height)d.%(ext)s'), '1080.mp4')
645 self
.assertEqual(fname('%(height)6d.%(ext)s'), ' 1080.mp4')
646 self
.assertEqual(fname('%(height)-6d.%(ext)s'), '1080 .mp4')
647 self
.assertEqual(fname('%(height)06d.%(ext)s'), '001080.mp4')
648 self
.assertEqual(fname('%(height) 06d.%(ext)s'), ' 01080.mp4')
649 self
.assertEqual(fname('%(height) 06d.%(ext)s'), ' 01080.mp4')
650 self
.assertEqual(fname('%(height)0 6d.%(ext)s'), ' 01080.mp4')
651 self
.assertEqual(fname('%(height)0 6d.%(ext)s'), ' 01080.mp4')
652 self
.assertEqual(fname('%(height) 0 6d.%(ext)s'), ' 01080.mp4')
653 self
.assertEqual(fname('%%'), '%')
654 self
.assertEqual(fname('%%%%'), '%%')
655 self
.assertEqual(fname('%%(height)06d.%(ext)s'), '%(height)06d.mp4')
656 self
.assertEqual(fname('%(width)06d.%(ext)s'), 'NA.mp4')
657 self
.assertEqual(fname('%(width)06d.%%(ext)s'), 'NA.%(ext)s')
658 self
.assertEqual(fname('%%(width)06d.%(ext)s'), '%(width)06d.mp4')
659 self
.assertEqual(fname('Hello %(title1)s'), 'Hello $PATH')
660 self
.assertEqual(fname('Hello %(title2)s'), 'Hello %PATH%')
662 def test_format_note(self
):
664 self
.assertEqual(ydl
._format
_note
({}), '')
665 assertRegexpMatches(self
, ydl
._format
_note
({
668 assertRegexpMatches(self
, ydl
._format
_note
({
672 def test_postprocessors(self
):
673 filename
= 'post-processor-testfile.mp4'
674 audiofile
= filename
+ '.mp3'
676 class SimplePP(PostProcessor
):
678 with open(audiofile
, 'wt') as f
:
680 return [info
['filepath']], info
682 def run_pp(params
, PP
):
683 with open(filename
, 'wt') as f
:
685 ydl
= YoutubeDL(params
)
686 ydl
.add_post_processor(PP())
687 ydl
.post_process(filename
, {'filepath': filename}
)
689 run_pp({'keepvideo': True}
, SimplePP
)
690 self
.assertTrue(os
.path
.exists(filename
), '%s doesn\'t exist' % filename
)
691 self
.assertTrue(os
.path
.exists(audiofile
), '%s doesn\'t exist' % audiofile
)
695 run_pp({'keepvideo': False}
, SimplePP
)
696 self
.assertFalse(os
.path
.exists(filename
), '%s exists' % filename
)
697 self
.assertTrue(os
.path
.exists(audiofile
), '%s doesn\'t exist' % audiofile
)
700 class ModifierPP(PostProcessor
):
702 with open(info
['filepath'], 'wt') as f
:
706 run_pp({'keepvideo': False}
, ModifierPP
)
707 self
.assertTrue(os
.path
.exists(filename
), '%s doesn\'t exist' % filename
)
710 def test_match_filter(self
):
711 class FilterYDL(YDL
):
712 def __init__(self
, *args
, **kwargs
):
713 super(FilterYDL
, self
).__init
__(*args
, **kwargs
)
714 self
.params
['simulate'] = True
716 def process_info(self
, info_dict
):
717 super(YDL
, self
).process_info(info_dict
)
719 def _match_entry(self
, info_dict
, incomplete
):
720 res
= super(FilterYDL
, self
)._match
_entry
(info_dict
, incomplete
)
722 self
.downloaded_info_dicts
.append(info_dict
)
731 'filesize': 10 * 1024,
733 'uploader': "變態妍字幕版 太妍 тест",
734 'creator': "тест ' 123 ' тест--",
735 'webpage_url': 'http://example.com/watch?v=shenanigans',
743 'description': 'foo',
744 'filesize': 5 * 1024,
746 'uploader': "тест 123",
747 'webpage_url': 'http://example.com/watch?v=SHENANIGANS',
749 videos
= [first
, second
]
751 def get_videos(filter_
=None):
752 ydl
= FilterYDL({'match_filter': filter_}
)
754 ydl
.process_ie_result(v
, download
=True)
755 return [v
['id'] for v
in ydl
.downloaded_info_dicts
]
758 self
.assertEqual(res
, ['1', '2'])
764 return 'Video id is not 1'
766 self
.assertEqual(res
, ['1'])
768 f
= match_filter_func('duration < 30')
770 self
.assertEqual(res
, ['2'])
772 f
= match_filter_func('description = foo')
774 self
.assertEqual(res
, ['2'])
776 f
= match_filter_func('description =? foo')
778 self
.assertEqual(res
, ['1', '2'])
780 f
= match_filter_func('filesize > 5KiB')
782 self
.assertEqual(res
, ['1'])
784 f
= match_filter_func('playlist_id = 42')
786 self
.assertEqual(res
, ['1'])
788 f
= match_filter_func('uploader = "變態妍字幕版 太妍 тест"')
790 self
.assertEqual(res
, ['1'])
792 f
= match_filter_func('uploader != "變態妍字幕版 太妍 тест"')
794 self
.assertEqual(res
, ['2'])
796 f
= match_filter_func('creator = "тест \' 123 \' тест--"')
798 self
.assertEqual(res
, ['1'])
800 f
= match_filter_func("creator = 'тест \\' 123 \\' тест--'")
802 self
.assertEqual(res
, ['1'])
804 f
= match_filter_func(r
"creator = 'тест \' 123 \' тест--' & duration > 30")
806 self
.assertEqual(res
, [])
808 def test_playlist_items_selection(self
):
811 'title': compat_str(i
),
813 } for i
in range(1, 5)]
818 'extractor': 'test:playlist',
819 'extractor_key': 'test:playlist',
820 'webpage_url': 'http://example.com',
823 def get_downloaded_info_dicts(params
):
825 # make a deep copy because the dictionary and nested entries
827 ydl
.process_ie_result(copy
.deepcopy(playlist
))
828 return ydl
.downloaded_info_dicts
831 return [int(v
['id']) for v
in get_downloaded_info_dicts(params
)]
834 self
.assertEqual(result
, [1, 2, 3, 4])
836 result
= get_ids({'playlistend': 10}
)
837 self
.assertEqual(result
, [1, 2, 3, 4])
839 result
= get_ids({'playlistend': 2}
)
840 self
.assertEqual(result
, [1, 2])
842 result
= get_ids({'playliststart': 10}
)
843 self
.assertEqual(result
, [])
845 result
= get_ids({'playliststart': 2}
)
846 self
.assertEqual(result
, [2, 3, 4])
848 result
= get_ids({'playlist_items': '2-4'}
)
849 self
.assertEqual(result
, [2, 3, 4])
851 result
= get_ids({'playlist_items': '2,4'}
)
852 self
.assertEqual(result
, [2, 4])
854 result
= get_ids({'playlist_items': '10'}
)
855 self
.assertEqual(result
, [])
857 result
= get_ids({'playlist_items': '3-10'}
)
858 self
.assertEqual(result
, [3, 4])
860 result
= get_ids({'playlist_items': '2-4,3-4,3'}
)
861 self
.assertEqual(result
, [2, 3, 4])
863 # Tests for https://github.com/ytdl-org/youtube-dl/issues/10591
865 result
= get_downloaded_info_dicts({'playlist_items': '2-4,3-4,3'}
)
866 self
.assertEqual(result
[0]['playlist_index'], 2)
867 self
.assertEqual(result
[1]['playlist_index'], 3)
869 result
= get_downloaded_info_dicts({'playlist_items': '2-4,3-4,3'}
)
870 self
.assertEqual(result
[0]['playlist_index'], 2)
871 self
.assertEqual(result
[1]['playlist_index'], 3)
872 self
.assertEqual(result
[2]['playlist_index'], 4)
874 result
= get_downloaded_info_dicts({'playlist_items': '4,2'}
)
875 self
.assertEqual(result
[0]['playlist_index'], 4)
876 self
.assertEqual(result
[1]['playlist_index'], 2)
879 def test_urlopen_no_file_protocol(self
):
880 # see https://github.com/ytdl-org/youtube-dl/issues/8227
882 self
.assertRaises(compat_urllib_error
.URLError
, ydl
.urlopen
, 'file:///etc/passwd')
884 def test_do_not_override_ie_key_in_url_transparent(self
):
887 class Foo1IE(InfoExtractor
):
888 _VALID_URL
= r
'foo1:'
890 def _real_extract(self
, url
):
892 '_type': 'url_transparent',
895 'title': 'foo1 title',
899 class Foo2IE(InfoExtractor
):
900 _VALID_URL
= r
'foo2:'
902 def _real_extract(self
, url
):
909 class Foo3IE(InfoExtractor
):
910 _VALID_URL
= r
'foo3:'
912 def _real_extract(self
, url
):
913 return _make_result([{'url': TEST_URL}
], title
='foo3 title')
915 ydl
.add_info_extractor(Foo1IE(ydl
))
916 ydl
.add_info_extractor(Foo2IE(ydl
))
917 ydl
.add_info_extractor(Foo3IE(ydl
))
918 ydl
.extract_info('foo1:')
919 downloaded
= ydl
.downloaded_info_dicts
[0]
920 self
.assertEqual(downloaded
['url'], TEST_URL
)
921 self
.assertEqual(downloaded
['title'], 'foo1 title')
922 self
.assertEqual(downloaded
['id'], 'testid')
923 self
.assertEqual(downloaded
['extractor'], 'testex')
924 self
.assertEqual(downloaded
['extractor_key'], 'TestEx')
926 # Test case for https://github.com/ytdl-org/youtube-dl/issues/27064
927 def test_ignoreerrors_for_playlist_with_url_transparent_iterable_entries(self
):
930 def __init__(self
, *args
, **kwargs
):
931 super(_YDL
, self
).__init
__(*args
, **kwargs
)
933 def trouble(self
, s
, tb
=None):
938 'ignoreerrors': True,
941 class VideoIE(InfoExtractor
):
942 _VALID_URL
= r
'video:(?P<id>\d+)'
944 def _real_extract(self
, url
):
945 video_id
= self
._match
_id
(url
)
947 'format_id': 'default',
951 raise ExtractorError('foo')
954 'format_id': 'extra',
959 'title': 'Video %s' % video_id
,
963 class PlaylistIE(InfoExtractor
):
964 _VALID_URL
= r
'playlist:'
968 video_id
= compat_str(n
)
970 '_type': 'url_transparent',
971 'ie_key': VideoIE
.ie_key(),
973 'url': 'video:%s' % video_id
,
974 'title': 'Video Transparent %s' % video_id
,
977 def _real_extract(self
, url
):
978 return self
.playlist_result(self
._entries
())
980 ydl
.add_info_extractor(VideoIE(ydl
))
981 ydl
.add_info_extractor(PlaylistIE(ydl
))
982 info
= ydl
.extract_info('playlist:')
983 entries
= info
['entries']
984 self
.assertEqual(len(entries
), 3)
985 self
.assertTrue(entries
[0] is None)
986 self
.assertTrue(entries
[1] is None)
987 self
.assertEqual(len(ydl
.downloaded_info_dicts
), 1)
988 downloaded
= ydl
.downloaded_info_dicts
[0]
989 self
.assertEqual(entries
[2], downloaded
)
990 self
.assertEqual(downloaded
['url'], TEST_URL
)
991 self
.assertEqual(downloaded
['title'], 'Video Transparent 2')
992 self
.assertEqual(downloaded
['id'], '2')
993 self
.assertEqual(downloaded
['extractor'], 'Video')
994 self
.assertEqual(downloaded
['extractor_key'], 'Video')
997 if __name__
== '__main__':