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 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
):
315 # disabled for now - this needs some changes
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
):
641 ydl
= YoutubeDL({'outtmpl': templ}
)
642 return ydl
.prepare_filename(info
)
643 self
.assertEqual(fname('%(id)s.%(ext)s'), '1234.mp4')
644 self
.assertEqual(fname('%(id)s-%(width)s.%(ext)s'), '1234-NA.mp4')
645 # Replace missing fields with 'NA'
646 self
.assertEqual(fname('%(uploader_date)s-%(id)s.%(ext)s'), 'NA-1234.mp4')
647 self
.assertEqual(fname('%(height)d.%(ext)s'), '1080.mp4')
648 self
.assertEqual(fname('%(height)6d.%(ext)s'), ' 1080.mp4')
649 self
.assertEqual(fname('%(height)-6d.%(ext)s'), '1080 .mp4')
650 self
.assertEqual(fname('%(height)06d.%(ext)s'), '001080.mp4')
651 self
.assertEqual(fname('%(height) 06d.%(ext)s'), ' 01080.mp4')
652 self
.assertEqual(fname('%(height) 06d.%(ext)s'), ' 01080.mp4')
653 self
.assertEqual(fname('%(height)0 6d.%(ext)s'), ' 01080.mp4')
654 self
.assertEqual(fname('%(height)0 6d.%(ext)s'), ' 01080.mp4')
655 self
.assertEqual(fname('%(height) 0 6d.%(ext)s'), ' 01080.mp4')
656 self
.assertEqual(fname('%%'), '%')
657 self
.assertEqual(fname('%%%%'), '%%')
658 self
.assertEqual(fname('%%(height)06d.%(ext)s'), '%(height)06d.mp4')
659 self
.assertEqual(fname('%(width)06d.%(ext)s'), 'NA.mp4')
660 self
.assertEqual(fname('%(width)06d.%%(ext)s'), 'NA.%(ext)s')
661 self
.assertEqual(fname('%%(width)06d.%(ext)s'), '%(width)06d.mp4')
662 self
.assertEqual(fname('Hello %(title1)s'), 'Hello $PATH')
663 self
.assertEqual(fname('Hello %(title2)s'), 'Hello %PATH%')
665 def test_format_note(self
):
667 self
.assertEqual(ydl
._format
_note
({}), '')
668 assertRegexpMatches(self
, ydl
._format
_note
({
671 assertRegexpMatches(self
, ydl
._format
_note
({
675 def test_postprocessors(self
):
676 filename
= 'post-processor-testfile.mp4'
677 audiofile
= filename
+ '.mp3'
679 class SimplePP(PostProcessor
):
681 with open(audiofile
, 'wt') as f
:
683 return [info
['filepath']], info
685 def run_pp(params
, PP
):
686 with open(filename
, 'wt') as f
:
688 ydl
= YoutubeDL(params
)
689 ydl
.add_post_processor(PP())
690 ydl
.post_process(filename
, {'filepath': filename}
)
692 run_pp({'keepvideo': True}
, SimplePP
)
693 self
.assertTrue(os
.path
.exists(filename
), '%s doesn\'t exist' % filename
)
694 self
.assertTrue(os
.path
.exists(audiofile
), '%s doesn\'t exist' % audiofile
)
698 run_pp({'keepvideo': False}
, SimplePP
)
699 self
.assertFalse(os
.path
.exists(filename
), '%s exists' % filename
)
700 self
.assertTrue(os
.path
.exists(audiofile
), '%s doesn\'t exist' % audiofile
)
703 class ModifierPP(PostProcessor
):
705 with open(info
['filepath'], 'wt') as f
:
709 run_pp({'keepvideo': False}
, ModifierPP
)
710 self
.assertTrue(os
.path
.exists(filename
), '%s doesn\'t exist' % filename
)
713 def test_match_filter(self
):
714 class FilterYDL(YDL
):
715 def __init__(self
, *args
, **kwargs
):
716 super(FilterYDL
, self
).__init
__(*args
, **kwargs
)
717 self
.params
['simulate'] = True
719 def process_info(self
, info_dict
):
720 super(YDL
, self
).process_info(info_dict
)
722 def _match_entry(self
, info_dict
, incomplete
):
723 res
= super(FilterYDL
, self
)._match
_entry
(info_dict
, incomplete
)
725 self
.downloaded_info_dicts
.append(info_dict
)
734 'filesize': 10 * 1024,
736 'uploader': "變態妍字幕版 太妍 тест",
737 'creator': "тест ' 123 ' тест--",
738 'webpage_url': 'http://example.com/watch?v=shenanigans',
746 'description': 'foo',
747 'filesize': 5 * 1024,
749 'uploader': "тест 123",
750 'webpage_url': 'http://example.com/watch?v=SHENANIGANS',
752 videos
= [first
, second
]
754 def get_videos(filter_
=None):
755 ydl
= FilterYDL({'match_filter': filter_}
)
757 ydl
.process_ie_result(v
, download
=True)
758 return [v
['id'] for v
in ydl
.downloaded_info_dicts
]
761 self
.assertEqual(res
, ['1', '2'])
767 return 'Video id is not 1'
769 self
.assertEqual(res
, ['1'])
771 f
= match_filter_func('duration < 30')
773 self
.assertEqual(res
, ['2'])
775 f
= match_filter_func('description = foo')
777 self
.assertEqual(res
, ['2'])
779 f
= match_filter_func('description =? foo')
781 self
.assertEqual(res
, ['1', '2'])
783 f
= match_filter_func('filesize > 5KiB')
785 self
.assertEqual(res
, ['1'])
787 f
= match_filter_func('playlist_id = 42')
789 self
.assertEqual(res
, ['1'])
791 f
= match_filter_func('uploader = "變態妍字幕版 太妍 тест"')
793 self
.assertEqual(res
, ['1'])
795 f
= match_filter_func('uploader != "變態妍字幕版 太妍 тест"')
797 self
.assertEqual(res
, ['2'])
799 f
= match_filter_func('creator = "тест \' 123 \' тест--"')
801 self
.assertEqual(res
, ['1'])
803 f
= match_filter_func("creator = 'тест \\' 123 \\' тест--'")
805 self
.assertEqual(res
, ['1'])
807 f
= match_filter_func(r
"creator = 'тест \' 123 \' тест--' & duration > 30")
809 self
.assertEqual(res
, [])
811 def test_playlist_items_selection(self
):
814 'title': compat_str(i
),
816 } for i
in range(1, 5)]
821 'extractor': 'test:playlist',
822 'extractor_key': 'test:playlist',
823 'webpage_url': 'http://example.com',
826 def get_downloaded_info_dicts(params
):
828 # make a deep copy because the dictionary and nested entries
830 ydl
.process_ie_result(copy
.deepcopy(playlist
))
831 return ydl
.downloaded_info_dicts
834 return [int(v
['id']) for v
in get_downloaded_info_dicts(params
)]
837 self
.assertEqual(result
, [1, 2, 3, 4])
839 result
= get_ids({'playlistend': 10}
)
840 self
.assertEqual(result
, [1, 2, 3, 4])
842 result
= get_ids({'playlistend': 2}
)
843 self
.assertEqual(result
, [1, 2])
845 result
= get_ids({'playliststart': 10}
)
846 self
.assertEqual(result
, [])
848 result
= get_ids({'playliststart': 2}
)
849 self
.assertEqual(result
, [2, 3, 4])
851 result
= get_ids({'playlist_items': '2-4'}
)
852 self
.assertEqual(result
, [2, 3, 4])
854 result
= get_ids({'playlist_items': '2,4'}
)
855 self
.assertEqual(result
, [2, 4])
857 result
= get_ids({'playlist_items': '10'}
)
858 self
.assertEqual(result
, [])
860 result
= get_ids({'playlist_items': '3-10'}
)
861 self
.assertEqual(result
, [3, 4])
863 result
= get_ids({'playlist_items': '2-4,3-4,3'}
)
864 self
.assertEqual(result
, [2, 3, 4])
866 # Tests for https://github.com/ytdl-org/youtube-dl/issues/10591
868 result
= get_downloaded_info_dicts({'playlist_items': '2-4,3-4,3'}
)
869 self
.assertEqual(result
[0]['playlist_index'], 2)
870 self
.assertEqual(result
[1]['playlist_index'], 3)
872 result
= get_downloaded_info_dicts({'playlist_items': '2-4,3-4,3'}
)
873 self
.assertEqual(result
[0]['playlist_index'], 2)
874 self
.assertEqual(result
[1]['playlist_index'], 3)
875 self
.assertEqual(result
[2]['playlist_index'], 4)
877 result
= get_downloaded_info_dicts({'playlist_items': '4,2'}
)
878 self
.assertEqual(result
[0]['playlist_index'], 4)
879 self
.assertEqual(result
[1]['playlist_index'], 2)
882 def test_urlopen_no_file_protocol(self
):
883 # see https://github.com/ytdl-org/youtube-dl/issues/8227
885 self
.assertRaises(compat_urllib_error
.URLError
, ydl
.urlopen
, 'file:///etc/passwd')
887 def test_do_not_override_ie_key_in_url_transparent(self
):
890 class Foo1IE(InfoExtractor
):
891 _VALID_URL
= r
'foo1:'
893 def _real_extract(self
, url
):
895 '_type': 'url_transparent',
898 'title': 'foo1 title',
902 class Foo2IE(InfoExtractor
):
903 _VALID_URL
= r
'foo2:'
905 def _real_extract(self
, url
):
912 class Foo3IE(InfoExtractor
):
913 _VALID_URL
= r
'foo3:'
915 def _real_extract(self
, url
):
916 return _make_result([{'url': TEST_URL}
], title
='foo3 title')
918 ydl
.add_info_extractor(Foo1IE(ydl
))
919 ydl
.add_info_extractor(Foo2IE(ydl
))
920 ydl
.add_info_extractor(Foo3IE(ydl
))
921 ydl
.extract_info('foo1:')
922 downloaded
= ydl
.downloaded_info_dicts
[0]
923 self
.assertEqual(downloaded
['url'], TEST_URL
)
924 self
.assertEqual(downloaded
['title'], 'foo1 title')
925 self
.assertEqual(downloaded
['id'], 'testid')
926 self
.assertEqual(downloaded
['extractor'], 'testex')
927 self
.assertEqual(downloaded
['extractor_key'], 'TestEx')
929 # Test case for https://github.com/ytdl-org/youtube-dl/issues/27064
930 def test_ignoreerrors_for_playlist_with_url_transparent_iterable_entries(self
):
933 def __init__(self
, *args
, **kwargs
):
934 super(_YDL
, self
).__init
__(*args
, **kwargs
)
936 def trouble(self
, s
, tb
=None):
941 'ignoreerrors': True,
944 class VideoIE(InfoExtractor
):
945 _VALID_URL
= r
'video:(?P<id>\d+)'
947 def _real_extract(self
, url
):
948 video_id
= self
._match
_id
(url
)
950 'format_id': 'default',
954 raise ExtractorError('foo')
957 'format_id': 'extra',
962 'title': 'Video %s' % video_id
,
966 class PlaylistIE(InfoExtractor
):
967 _VALID_URL
= r
'playlist:'
971 video_id
= compat_str(n
)
973 '_type': 'url_transparent',
974 'ie_key': VideoIE
.ie_key(),
976 'url': 'video:%s' % video_id
,
977 'title': 'Video Transparent %s' % video_id
,
980 def _real_extract(self
, url
):
981 return self
.playlist_result(self
._entries
())
983 ydl
.add_info_extractor(VideoIE(ydl
))
984 ydl
.add_info_extractor(PlaylistIE(ydl
))
985 info
= ydl
.extract_info('playlist:')
986 entries
= info
['entries']
987 self
.assertEqual(len(entries
), 3)
988 self
.assertTrue(entries
[0] is None)
989 self
.assertTrue(entries
[1] is None)
990 self
.assertEqual(len(ydl
.downloaded_info_dicts
), 1)
991 downloaded
= ydl
.downloaded_info_dicts
[0]
992 self
.assertEqual(entries
[2], downloaded
)
993 self
.assertEqual(downloaded
['url'], TEST_URL
)
994 self
.assertEqual(downloaded
['title'], 'Video Transparent 2')
995 self
.assertEqual(downloaded
['id'], '2')
996 self
.assertEqual(downloaded
['extractor'], 'Video')
997 self
.assertEqual(downloaded
['extractor_key'], 'Video')
1000 if __name__
== '__main__':