3 from __future__
import unicode_literals
5 # Allow direct execution
9 sys
.path
.insert(0, os
.path
.dirname(os
.path
.dirname(os
.path
.abspath(__file__
))))
13 from test
.helper
import FakeYDL
, assertRegexpMatches
14 from youtube_dl
import YoutubeDL
15 from youtube_dl
.extractor
import YoutubeIE
16 from youtube_dl
.postprocessor
.common
import PostProcessor
17 from youtube_dl
.utils
import match_filter_func
19 TEST_URL
= 'http://localhost/sample.mp4'
23 def __init__(self
, *args
, **kwargs
):
24 super(YDL
, self
).__init
__(*args
, **kwargs
)
25 self
.downloaded_info_dicts
= []
28 def process_info(self
, info_dict
):
29 self
.downloaded_info_dicts
.append(info_dict
)
31 def to_screen(self
, msg
):
35 def _make_result(formats
, **kwargs
):
39 'title': 'testttitle',
40 'extractor': 'testex',
46 class TestFormatSelection(unittest
.TestCase
):
47 def test_prefer_free_formats(self
):
48 # Same resolution => download webm
50 ydl
.params
['prefer_free_formats'] = True
52 {'ext': 'webm', 'height': 460, 'url': TEST_URL}
,
53 {'ext': 'mp4', 'height': 460, 'url': TEST_URL}
,
55 info_dict
= _make_result(formats
)
57 yie
._sort
_formats
(info_dict
['formats'])
58 ydl
.process_ie_result(info_dict
)
59 downloaded
= ydl
.downloaded_info_dicts
[0]
60 self
.assertEqual(downloaded
['ext'], 'webm')
62 # Different resolution => download best quality (mp4)
64 ydl
.params
['prefer_free_formats'] = True
66 {'ext': 'webm', 'height': 720, 'url': TEST_URL}
,
67 {'ext': 'mp4', 'height': 1080, 'url': TEST_URL}
,
69 info_dict
['formats'] = formats
71 yie
._sort
_formats
(info_dict
['formats'])
72 ydl
.process_ie_result(info_dict
)
73 downloaded
= ydl
.downloaded_info_dicts
[0]
74 self
.assertEqual(downloaded
['ext'], 'mp4')
76 # No prefer_free_formats => prefer mp4 and flv for greater compatibility
78 ydl
.params
['prefer_free_formats'] = False
80 {'ext': 'webm', 'height': 720, 'url': TEST_URL}
,
81 {'ext': 'mp4', 'height': 720, 'url': TEST_URL}
,
82 {'ext': 'flv', 'height': 720, 'url': TEST_URL}
,
84 info_dict
['formats'] = formats
86 yie
._sort
_formats
(info_dict
['formats'])
87 ydl
.process_ie_result(info_dict
)
88 downloaded
= ydl
.downloaded_info_dicts
[0]
89 self
.assertEqual(downloaded
['ext'], 'mp4')
92 ydl
.params
['prefer_free_formats'] = False
94 {'ext': 'flv', 'height': 720, 'url': TEST_URL}
,
95 {'ext': 'webm', 'height': 720, 'url': TEST_URL}
,
97 info_dict
['formats'] = formats
99 yie
._sort
_formats
(info_dict
['formats'])
100 ydl
.process_ie_result(info_dict
)
101 downloaded
= ydl
.downloaded_info_dicts
[0]
102 self
.assertEqual(downloaded
['ext'], 'flv')
104 def test_format_limit(self
):
106 {'format_id': 'meh', 'url': 'http://example.com/meh', 'preference': 1}
,
107 {'format_id': 'good', 'url': 'http://example.com/good', 'preference': 2}
,
108 {'format_id': 'great', 'url': 'http://example.com/great', 'preference': 3}
,
109 {'format_id': 'excellent', 'url': 'http://example.com/exc', 'preference': 4}
,
111 info_dict
= _make_result(formats
)
114 ydl
.process_ie_result(info_dict
)
115 downloaded
= ydl
.downloaded_info_dicts
[0]
116 self
.assertEqual(downloaded
['format_id'], 'excellent')
118 ydl
= YDL({'format_limit': 'good'}
)
119 assert ydl
.params
['format_limit'] == 'good'
120 ydl
.process_ie_result(info_dict
.copy())
121 downloaded
= ydl
.downloaded_info_dicts
[0]
122 self
.assertEqual(downloaded
['format_id'], 'good')
124 ydl
= YDL({'format_limit': 'great', 'format': 'all'}
)
125 ydl
.process_ie_result(info_dict
.copy())
126 self
.assertEqual(ydl
.downloaded_info_dicts
[0]['format_id'], 'meh')
127 self
.assertEqual(ydl
.downloaded_info_dicts
[1]['format_id'], 'good')
128 self
.assertEqual(ydl
.downloaded_info_dicts
[2]['format_id'], 'great')
129 self
.assertTrue('3' in ydl
.msgs
[0])
132 ydl
.params
['format_limit'] = 'excellent'
133 ydl
.process_ie_result(info_dict
.copy())
134 downloaded
= ydl
.downloaded_info_dicts
[0]
135 self
.assertEqual(downloaded
['format_id'], 'excellent')
137 def test_format_selection(self
):
139 {'format_id': '35', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL}
,
140 {'format_id': '45', 'ext': 'webm', 'preference': 2, 'url': TEST_URL}
,
141 {'format_id': '47', 'ext': 'webm', 'preference': 3, 'url': TEST_URL}
,
142 {'format_id': '2', 'ext': 'flv', 'preference': 4, 'url': TEST_URL}
,
144 info_dict
= _make_result(formats
)
146 ydl
= YDL({'format': '20/47'}
)
147 ydl
.process_ie_result(info_dict
.copy())
148 downloaded
= ydl
.downloaded_info_dicts
[0]
149 self
.assertEqual(downloaded
['format_id'], '47')
151 ydl
= YDL({'format': '20/71/worst'}
)
152 ydl
.process_ie_result(info_dict
.copy())
153 downloaded
= ydl
.downloaded_info_dicts
[0]
154 self
.assertEqual(downloaded
['format_id'], '35')
157 ydl
.process_ie_result(info_dict
.copy())
158 downloaded
= ydl
.downloaded_info_dicts
[0]
159 self
.assertEqual(downloaded
['format_id'], '2')
161 ydl
= YDL({'format': 'webm/mp4'}
)
162 ydl
.process_ie_result(info_dict
.copy())
163 downloaded
= ydl
.downloaded_info_dicts
[0]
164 self
.assertEqual(downloaded
['format_id'], '47')
166 ydl
= YDL({'format': '3gp/40/mp4'}
)
167 ydl
.process_ie_result(info_dict
.copy())
168 downloaded
= ydl
.downloaded_info_dicts
[0]
169 self
.assertEqual(downloaded
['format_id'], '35')
171 def test_format_selection_audio(self
):
173 {'format_id': 'audio-low', 'ext': 'webm', 'preference': 1, 'vcodec': 'none', 'url': TEST_URL}
,
174 {'format_id': 'audio-mid', 'ext': 'webm', 'preference': 2, 'vcodec': 'none', 'url': TEST_URL}
,
175 {'format_id': 'audio-high', 'ext': 'flv', 'preference': 3, 'vcodec': 'none', 'url': TEST_URL}
,
176 {'format_id': 'vid', 'ext': 'mp4', 'preference': 4, 'url': TEST_URL}
,
178 info_dict
= _make_result(formats
)
180 ydl
= YDL({'format': 'bestaudio'}
)
181 ydl
.process_ie_result(info_dict
.copy())
182 downloaded
= ydl
.downloaded_info_dicts
[0]
183 self
.assertEqual(downloaded
['format_id'], 'audio-high')
185 ydl
= YDL({'format': 'worstaudio'}
)
186 ydl
.process_ie_result(info_dict
.copy())
187 downloaded
= ydl
.downloaded_info_dicts
[0]
188 self
.assertEqual(downloaded
['format_id'], 'audio-low')
191 {'format_id': 'vid-low', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL}
,
192 {'format_id': 'vid-high', 'ext': 'mp4', 'preference': 2, 'url': TEST_URL}
,
194 info_dict
= _make_result(formats
)
196 ydl
= YDL({'format': 'bestaudio/worstaudio/best'}
)
197 ydl
.process_ie_result(info_dict
.copy())
198 downloaded
= ydl
.downloaded_info_dicts
[0]
199 self
.assertEqual(downloaded
['format_id'], 'vid-high')
201 def test_format_selection_audio_exts(self
):
203 {'format_id': 'mp3-64', 'ext': 'mp3', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'}
,
204 {'format_id': 'ogg-64', 'ext': 'ogg', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'}
,
205 {'format_id': 'aac-64', 'ext': 'aac', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'}
,
206 {'format_id': 'mp3-32', 'ext': 'mp3', 'abr': 32, 'url': 'http://_', 'vcodec': 'none'}
,
207 {'format_id': 'aac-32', 'ext': 'aac', 'abr': 32, 'url': 'http://_', 'vcodec': 'none'}
,
210 info_dict
= _make_result(formats
)
211 ydl
= YDL({'format': 'best'}
)
213 ie
._sort
_formats
(info_dict
['formats'])
214 ydl
.process_ie_result(copy
.deepcopy(info_dict
))
215 downloaded
= ydl
.downloaded_info_dicts
[0]
216 self
.assertEqual(downloaded
['format_id'], 'aac-64')
218 ydl
= YDL({'format': 'mp3'}
)
220 ie
._sort
_formats
(info_dict
['formats'])
221 ydl
.process_ie_result(copy
.deepcopy(info_dict
))
222 downloaded
= ydl
.downloaded_info_dicts
[0]
223 self
.assertEqual(downloaded
['format_id'], 'mp3-64')
225 ydl
= YDL({'prefer_free_formats': True}
)
227 ie
._sort
_formats
(info_dict
['formats'])
228 ydl
.process_ie_result(copy
.deepcopy(info_dict
))
229 downloaded
= ydl
.downloaded_info_dicts
[0]
230 self
.assertEqual(downloaded
['format_id'], 'ogg-64')
232 def test_format_selection_video(self
):
234 {'format_id': 'dash-video-low', 'ext': 'mp4', 'preference': 1, 'acodec': 'none', 'url': TEST_URL}
,
235 {'format_id': 'dash-video-high', 'ext': 'mp4', 'preference': 2, 'acodec': 'none', 'url': TEST_URL}
,
236 {'format_id': 'vid', 'ext': 'mp4', 'preference': 3, 'url': TEST_URL}
,
238 info_dict
= _make_result(formats
)
240 ydl
= YDL({'format': 'bestvideo'}
)
241 ydl
.process_ie_result(info_dict
.copy())
242 downloaded
= ydl
.downloaded_info_dicts
[0]
243 self
.assertEqual(downloaded
['format_id'], 'dash-video-high')
245 ydl
= YDL({'format': 'worstvideo'}
)
246 ydl
.process_ie_result(info_dict
.copy())
247 downloaded
= ydl
.downloaded_info_dicts
[0]
248 self
.assertEqual(downloaded
['format_id'], 'dash-video-low')
250 def test_youtube_format_selection(self
):
252 '38', '37', '46', '22', '45', '35', '44', '18', '34', '43', '6', '5', '36', '17', '13',
253 # Apple HTTP Live Streaming
254 '96', '95', '94', '93', '92', '132', '151',
256 '85', '84', '102', '83', '101', '82', '100',
258 '137', '248', '136', '247', '135', '246',
259 '245', '244', '134', '243', '133', '242', '160',
261 '141', '172', '140', '171', '139',
264 for f1id
, f2id
in zip(order
, order
[1:]):
265 f1
= YoutubeIE
._formats
[f1id
].copy()
266 f1
['format_id'] = f1id
267 f1
['url'] = 'url:' + f1id
268 f2
= YoutubeIE
._formats
[f2id
].copy()
269 f2
['format_id'] = f2id
270 f2
['url'] = 'url:' + f2id
272 info_dict
= _make_result([f1
, f2
], extractor
='youtube')
275 yie
._sort
_formats
(info_dict
['formats'])
276 ydl
.process_ie_result(info_dict
)
277 downloaded
= ydl
.downloaded_info_dicts
[0]
278 self
.assertEqual(downloaded
['format_id'], f1id
)
280 info_dict
= _make_result([f2
, f1
], extractor
='youtube')
283 yie
._sort
_formats
(info_dict
['formats'])
284 ydl
.process_ie_result(info_dict
)
285 downloaded
= ydl
.downloaded_info_dicts
[0]
286 self
.assertEqual(downloaded
['format_id'], f1id
)
288 def test_format_filtering(self
):
290 {'format_id': 'A', 'filesize': 500, 'width': 1000}
,
291 {'format_id': 'B', 'filesize': 1000, 'width': 500}
,
292 {'format_id': 'C', 'filesize': 1000, 'width': 400}
,
293 {'format_id': 'D', 'filesize': 2000, 'width': 600}
,
294 {'format_id': 'E', 'filesize': 3000}
,
296 {'format_id': 'G', 'filesize': 1000000}
,
299 f
['url'] = 'http://_/'
301 info_dict
= _make_result(formats
)
303 ydl
= YDL({'format': 'best[filesize<3000]'}
)
304 ydl
.process_ie_result(info_dict
)
305 downloaded
= ydl
.downloaded_info_dicts
[0]
306 self
.assertEqual(downloaded
['format_id'], 'D')
308 ydl
= YDL({'format': 'best[filesize<=3000]'}
)
309 ydl
.process_ie_result(info_dict
)
310 downloaded
= ydl
.downloaded_info_dicts
[0]
311 self
.assertEqual(downloaded
['format_id'], 'E')
313 ydl
= YDL({'format': 'best[filesize <= ? 3000]'}
)
314 ydl
.process_ie_result(info_dict
)
315 downloaded
= ydl
.downloaded_info_dicts
[0]
316 self
.assertEqual(downloaded
['format_id'], 'F')
318 ydl
= YDL({'format': 'best [filesize = 1000] [width>450]'}
)
319 ydl
.process_ie_result(info_dict
)
320 downloaded
= ydl
.downloaded_info_dicts
[0]
321 self
.assertEqual(downloaded
['format_id'], 'B')
323 ydl
= YDL({'format': 'best [filesize = 1000] [width!=450]'}
)
324 ydl
.process_ie_result(info_dict
)
325 downloaded
= ydl
.downloaded_info_dicts
[0]
326 self
.assertEqual(downloaded
['format_id'], 'C')
328 ydl
= YDL({'format': '[filesize>?1]'}
)
329 ydl
.process_ie_result(info_dict
)
330 downloaded
= ydl
.downloaded_info_dicts
[0]
331 self
.assertEqual(downloaded
['format_id'], 'G')
333 ydl
= YDL({'format': '[filesize<1M]'}
)
334 ydl
.process_ie_result(info_dict
)
335 downloaded
= ydl
.downloaded_info_dicts
[0]
336 self
.assertEqual(downloaded
['format_id'], 'E')
338 ydl
= YDL({'format': '[filesize<1MiB]'}
)
339 ydl
.process_ie_result(info_dict
)
340 downloaded
= ydl
.downloaded_info_dicts
[0]
341 self
.assertEqual(downloaded
['format_id'], 'G')
344 class TestYoutubeDL(unittest
.TestCase
):
345 def test_subtitles(self
):
346 def s_formats(lang
, autocaption
=False):
349 'url': 'http://localhost/video.%s.%s' % (lang
, ext
),
350 '_auto': autocaption
,
351 } for ext
in ['vtt', 'srt', 'ass']]
352 subtitles
= dict((l
, s_formats(l
)) for l
in ['en', 'fr', 'es'])
353 auto_captions
= dict((l
, s_formats(l
, True)) for l
in ['it', 'pt', 'es'])
357 'url': 'http://localhost/video.mp4',
358 'subtitles': subtitles
,
359 'automatic_captions': auto_captions
,
363 def get_info(params
={}):
364 params
.setdefault('simulate', True)
366 ydl
.report_warning
= lambda *args
, **kargs
: None
367 return ydl
.process_video_result(info_dict
, download
=False)
370 self
.assertFalse(result
.get('requested_subtitles'))
371 self
.assertEqual(result
['subtitles'], subtitles
)
372 self
.assertEqual(result
['automatic_captions'], auto_captions
)
374 result
= get_info({'writesubtitles': True}
)
375 subs
= result
['requested_subtitles']
376 self
.assertTrue(subs
)
377 self
.assertEqual(set(subs
.keys()), set(['en']))
378 self
.assertTrue(subs
['en'].get('data') is None)
379 self
.assertEqual(subs
['en']['ext'], 'ass')
381 result
= get_info({'writesubtitles': True, 'subtitlesformat': 'foo/srt'}
)
382 subs
= result
['requested_subtitles']
383 self
.assertEqual(subs
['en']['ext'], 'srt')
385 result
= get_info({'writesubtitles': True, 'subtitleslangs': ['es', 'fr', 'it']}
)
386 subs
= result
['requested_subtitles']
387 self
.assertTrue(subs
)
388 self
.assertEqual(set(subs
.keys()), set(['es', 'fr']))
390 result
= get_info({'writesubtitles': True, 'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']}
)
391 subs
= result
['requested_subtitles']
392 self
.assertTrue(subs
)
393 self
.assertEqual(set(subs
.keys()), set(['es', 'pt']))
394 self
.assertFalse(subs
['es']['_auto'])
395 self
.assertTrue(subs
['pt']['_auto'])
397 result
= get_info({'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']}
)
398 subs
= result
['requested_subtitles']
399 self
.assertTrue(subs
)
400 self
.assertEqual(set(subs
.keys()), set(['es', 'pt']))
401 self
.assertTrue(subs
['es']['_auto'])
402 self
.assertTrue(subs
['pt']['_auto'])
404 def test_add_extra_info(self
):
410 'playlist': 'funny videos',
412 YDL
.add_extra_info(test_dict
, extra_info
)
413 self
.assertEqual(test_dict
['extractor'], 'Foo')
414 self
.assertEqual(test_dict
['playlist'], 'funny videos')
416 def test_prepare_filename(self
):
424 ydl
= YoutubeDL({'outtmpl': templ}
)
425 return ydl
.prepare_filename(info
)
426 self
.assertEqual(fname('%(id)s.%(ext)s'), '1234.mp4')
427 self
.assertEqual(fname('%(id)s-%(width)s.%(ext)s'), '1234-NA.mp4')
428 # Replace missing fields with 'NA'
429 self
.assertEqual(fname('%(uploader_date)s-%(id)s.%(ext)s'), 'NA-1234.mp4')
431 def test_format_note(self
):
433 self
.assertEqual(ydl
._format
_note
({}), '')
434 assertRegexpMatches(self
, ydl
._format
_note
({
438 def test_postprocessors(self
):
439 filename
= 'post-processor-testfile.mp4'
440 audiofile
= filename
+ '.mp3'
442 class SimplePP(PostProcessor
):
444 with open(audiofile
, 'wt') as f
:
450 with open(filename
, 'wt') as f
:
452 ydl
= YoutubeDL(params
)
453 ydl
.add_post_processor(SimplePP())
454 ydl
.post_process(filename
, {'filepath': filename}
)
456 run_pp({'keepvideo': True}
)
457 self
.assertTrue(os
.path
.exists(filename
), '%s doesn\'t exist' % filename
)
458 self
.assertTrue(os
.path
.exists(audiofile
), '%s doesn\'t exist' % audiofile
)
462 run_pp({'keepvideo': False}
)
463 self
.assertFalse(os
.path
.exists(filename
), '%s exists' % filename
)
464 self
.assertTrue(os
.path
.exists(audiofile
), '%s doesn\'t exist' % audiofile
)
467 def test_match_filter(self
):
468 class FilterYDL(YDL
):
469 def __init__(self
, *args
, **kwargs
):
470 super(FilterYDL
, self
).__init
__(*args
, **kwargs
)
471 self
.params
['simulate'] = True
473 def process_info(self
, info_dict
):
474 super(YDL
, self
).process_info(info_dict
)
476 def _match_entry(self
, info_dict
, incomplete
):
477 res
= super(FilterYDL
, self
)._match
_entry
(info_dict
, incomplete
)
479 self
.downloaded_info_dicts
.append(info_dict
)
488 'filesize': 10 * 1024,
496 'description': 'foo',
497 'filesize': 5 * 1024,
499 videos
= [first
, second
]
501 def get_videos(filter_
=None):
502 ydl
= FilterYDL({'match_filter': filter_}
)
504 ydl
.process_ie_result(v
, download
=True)
505 return [v
['id'] for v
in ydl
.downloaded_info_dicts
]
508 self
.assertEqual(res
, ['1', '2'])
514 return 'Video id is not 1'
516 self
.assertEqual(res
, ['1'])
518 f
= match_filter_func('duration < 30')
520 self
.assertEqual(res
, ['2'])
522 f
= match_filter_func('description = foo')
524 self
.assertEqual(res
, ['2'])
526 f
= match_filter_func('description =? foo')
528 self
.assertEqual(res
, ['1', '2'])
530 f
= match_filter_func('filesize > 5KiB')
532 self
.assertEqual(res
, ['1'])
535 if __name__
== '__main__':