]> git.bitcoin.ninja Git - youtube-dl/blob - test/test_YoutubeDL.py
Explain why and why not to specify --hls-prefer-native
[youtube-dl] / test / test_YoutubeDL.py
1 #!/usr/bin/env python
2
3 from __future__ import unicode_literals
4
5 # Allow direct execution
6 import os
7 import sys
8 import unittest
9 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
10
11 import copy
12
13 from test.helper import FakeYDL, assertRegexpMatches
14 from youtube_dl import YoutubeDL
15 from youtube_dl.compat import compat_str, compat_urllib_error
16 from youtube_dl.extractor import YoutubeIE
17 from youtube_dl.extractor.common import InfoExtractor
18 from youtube_dl.postprocessor.common import PostProcessor
19 from youtube_dl.utils import ExtractorError, match_filter_func
20
21 TEST_URL = 'http://localhost/sample.mp4'
22
23
24 class YDL(FakeYDL):
25     def __init__(self, *args, **kwargs):
26         super(YDL, self).__init__(*args, **kwargs)
27         self.downloaded_info_dicts = []
28         self.msgs = []
29
30     def process_info(self, info_dict):
31         self.downloaded_info_dicts.append(info_dict)
32
33     def to_screen(self, msg):
34         self.msgs.append(msg)
35
36
37 def _make_result(formats, **kwargs):
38     res = {
39         'formats': formats,
40         'id': 'testid',
41         'title': 'testttitle',
42         'extractor': 'testex',
43     }
44     res.update(**kwargs)
45     return res
46
47
48 class TestFormatSelection(unittest.TestCase):
49     def test_prefer_free_formats(self):
50         # Same resolution => download webm
51         ydl = YDL()
52         ydl.params['prefer_free_formats'] = True
53         formats = [
54             {'ext': 'webm', 'height': 460, 'url': TEST_URL},
55             {'ext': 'mp4', 'height': 460, 'url': TEST_URL},
56         ]
57         info_dict = _make_result(formats)
58         yie = YoutubeIE(ydl)
59         yie._sort_formats(info_dict['formats'])
60         ydl.process_ie_result(info_dict)
61         downloaded = ydl.downloaded_info_dicts[0]
62         self.assertEqual(downloaded['ext'], 'webm')
63
64         # Different resolution => download best quality (mp4)
65         ydl = YDL()
66         ydl.params['prefer_free_formats'] = True
67         formats = [
68             {'ext': 'webm', 'height': 720, 'url': TEST_URL},
69             {'ext': 'mp4', 'height': 1080, 'url': TEST_URL},
70         ]
71         info_dict['formats'] = formats
72         yie = YoutubeIE(ydl)
73         yie._sort_formats(info_dict['formats'])
74         ydl.process_ie_result(info_dict)
75         downloaded = ydl.downloaded_info_dicts[0]
76         self.assertEqual(downloaded['ext'], 'mp4')
77
78         # No prefer_free_formats => prefer mp4 and flv for greater compatibility
79         ydl = YDL()
80         ydl.params['prefer_free_formats'] = False
81         formats = [
82             {'ext': 'webm', 'height': 720, 'url': TEST_URL},
83             {'ext': 'mp4', 'height': 720, 'url': TEST_URL},
84             {'ext': 'flv', 'height': 720, 'url': TEST_URL},
85         ]
86         info_dict['formats'] = formats
87         yie = YoutubeIE(ydl)
88         yie._sort_formats(info_dict['formats'])
89         ydl.process_ie_result(info_dict)
90         downloaded = ydl.downloaded_info_dicts[0]
91         self.assertEqual(downloaded['ext'], 'mp4')
92
93         ydl = YDL()
94         ydl.params['prefer_free_formats'] = False
95         formats = [
96             {'ext': 'flv', 'height': 720, 'url': TEST_URL},
97             {'ext': 'webm', 'height': 720, 'url': TEST_URL},
98         ]
99         info_dict['formats'] = formats
100         yie = YoutubeIE(ydl)
101         yie._sort_formats(info_dict['formats'])
102         ydl.process_ie_result(info_dict)
103         downloaded = ydl.downloaded_info_dicts[0]
104         self.assertEqual(downloaded['ext'], 'flv')
105
106     def test_format_selection(self):
107         formats = [
108             {'format_id': '35', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL},
109             {'format_id': 'example-with-dashes', 'ext': 'webm', 'preference': 1, 'url': TEST_URL},
110             {'format_id': '45', 'ext': 'webm', 'preference': 2, 'url': TEST_URL},
111             {'format_id': '47', 'ext': 'webm', 'preference': 3, 'url': TEST_URL},
112             {'format_id': '2', 'ext': 'flv', 'preference': 4, 'url': TEST_URL},
113         ]
114         info_dict = _make_result(formats)
115
116         ydl = YDL({'format': '20/47'})
117         ydl.process_ie_result(info_dict.copy())
118         downloaded = ydl.downloaded_info_dicts[0]
119         self.assertEqual(downloaded['format_id'], '47')
120
121         ydl = YDL({'format': '20/71/worst'})
122         ydl.process_ie_result(info_dict.copy())
123         downloaded = ydl.downloaded_info_dicts[0]
124         self.assertEqual(downloaded['format_id'], '35')
125
126         ydl = YDL()
127         ydl.process_ie_result(info_dict.copy())
128         downloaded = ydl.downloaded_info_dicts[0]
129         self.assertEqual(downloaded['format_id'], '2')
130
131         ydl = YDL({'format': 'webm/mp4'})
132         ydl.process_ie_result(info_dict.copy())
133         downloaded = ydl.downloaded_info_dicts[0]
134         self.assertEqual(downloaded['format_id'], '47')
135
136         ydl = YDL({'format': '3gp/40/mp4'})
137         ydl.process_ie_result(info_dict.copy())
138         downloaded = ydl.downloaded_info_dicts[0]
139         self.assertEqual(downloaded['format_id'], '35')
140
141         ydl = YDL({'format': 'example-with-dashes'})
142         ydl.process_ie_result(info_dict.copy())
143         downloaded = ydl.downloaded_info_dicts[0]
144         self.assertEqual(downloaded['format_id'], 'example-with-dashes')
145
146     def test_format_selection_audio(self):
147         formats = [
148             {'format_id': 'audio-low', 'ext': 'webm', 'preference': 1, 'vcodec': 'none', 'url': TEST_URL},
149             {'format_id': 'audio-mid', 'ext': 'webm', 'preference': 2, 'vcodec': 'none', 'url': TEST_URL},
150             {'format_id': 'audio-high', 'ext': 'flv', 'preference': 3, 'vcodec': 'none', 'url': TEST_URL},
151             {'format_id': 'vid', 'ext': 'mp4', 'preference': 4, 'url': TEST_URL},
152         ]
153         info_dict = _make_result(formats)
154
155         ydl = YDL({'format': 'bestaudio'})
156         ydl.process_ie_result(info_dict.copy())
157         downloaded = ydl.downloaded_info_dicts[0]
158         self.assertEqual(downloaded['format_id'], 'audio-high')
159
160         ydl = YDL({'format': 'worstaudio'})
161         ydl.process_ie_result(info_dict.copy())
162         downloaded = ydl.downloaded_info_dicts[0]
163         self.assertEqual(downloaded['format_id'], 'audio-low')
164
165         formats = [
166             {'format_id': 'vid-low', 'ext': 'mp4', 'preference': 1, 'url': TEST_URL},
167             {'format_id': 'vid-high', 'ext': 'mp4', 'preference': 2, 'url': TEST_URL},
168         ]
169         info_dict = _make_result(formats)
170
171         ydl = YDL({'format': 'bestaudio/worstaudio/best'})
172         ydl.process_ie_result(info_dict.copy())
173         downloaded = ydl.downloaded_info_dicts[0]
174         self.assertEqual(downloaded['format_id'], 'vid-high')
175
176     def test_format_selection_audio_exts(self):
177         formats = [
178             {'format_id': 'mp3-64', 'ext': 'mp3', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
179             {'format_id': 'ogg-64', 'ext': 'ogg', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
180             {'format_id': 'aac-64', 'ext': 'aac', 'abr': 64, 'url': 'http://_', 'vcodec': 'none'},
181             {'format_id': 'mp3-32', 'ext': 'mp3', 'abr': 32, 'url': 'http://_', 'vcodec': 'none'},
182             {'format_id': 'aac-32', 'ext': 'aac', 'abr': 32, 'url': 'http://_', 'vcodec': 'none'},
183         ]
184
185         info_dict = _make_result(formats)
186         ydl = YDL({'format': 'best'})
187         ie = YoutubeIE(ydl)
188         ie._sort_formats(info_dict['formats'])
189         ydl.process_ie_result(copy.deepcopy(info_dict))
190         downloaded = ydl.downloaded_info_dicts[0]
191         self.assertEqual(downloaded['format_id'], 'aac-64')
192
193         ydl = YDL({'format': 'mp3'})
194         ie = YoutubeIE(ydl)
195         ie._sort_formats(info_dict['formats'])
196         ydl.process_ie_result(copy.deepcopy(info_dict))
197         downloaded = ydl.downloaded_info_dicts[0]
198         self.assertEqual(downloaded['format_id'], 'mp3-64')
199
200         ydl = YDL({'prefer_free_formats': True})
201         ie = YoutubeIE(ydl)
202         ie._sort_formats(info_dict['formats'])
203         ydl.process_ie_result(copy.deepcopy(info_dict))
204         downloaded = ydl.downloaded_info_dicts[0]
205         self.assertEqual(downloaded['format_id'], 'ogg-64')
206
207     def test_format_selection_video(self):
208         formats = [
209             {'format_id': 'dash-video-low', 'ext': 'mp4', 'preference': 1, 'acodec': 'none', 'url': TEST_URL},
210             {'format_id': 'dash-video-high', 'ext': 'mp4', 'preference': 2, 'acodec': 'none', 'url': TEST_URL},
211             {'format_id': 'vid', 'ext': 'mp4', 'preference': 3, 'url': TEST_URL},
212         ]
213         info_dict = _make_result(formats)
214
215         ydl = YDL({'format': 'bestvideo'})
216         ydl.process_ie_result(info_dict.copy())
217         downloaded = ydl.downloaded_info_dicts[0]
218         self.assertEqual(downloaded['format_id'], 'dash-video-high')
219
220         ydl = YDL({'format': 'worstvideo'})
221         ydl.process_ie_result(info_dict.copy())
222         downloaded = ydl.downloaded_info_dicts[0]
223         self.assertEqual(downloaded['format_id'], 'dash-video-low')
224
225         ydl = YDL({'format': 'bestvideo[format_id^=dash][format_id$=low]'})
226         ydl.process_ie_result(info_dict.copy())
227         downloaded = ydl.downloaded_info_dicts[0]
228         self.assertEqual(downloaded['format_id'], 'dash-video-low')
229
230         formats = [
231             {'format_id': 'vid-vcodec-dot', 'ext': 'mp4', 'preference': 1, 'vcodec': 'avc1.123456', 'acodec': 'none', 'url': TEST_URL},
232         ]
233         info_dict = _make_result(formats)
234
235         ydl = YDL({'format': 'bestvideo[vcodec=avc1.123456]'})
236         ydl.process_ie_result(info_dict.copy())
237         downloaded = ydl.downloaded_info_dicts[0]
238         self.assertEqual(downloaded['format_id'], 'vid-vcodec-dot')
239
240     def test_youtube_format_selection(self):
241         order = [
242             '38', '37', '46', '22', '45', '35', '44', '18', '34', '43', '6', '5', '17', '36', '13',
243             # Apple HTTP Live Streaming
244             '96', '95', '94', '93', '92', '132', '151',
245             # 3D
246             '85', '84', '102', '83', '101', '82', '100',
247             # Dash video
248             '137', '248', '136', '247', '135', '246',
249             '245', '244', '134', '243', '133', '242', '160',
250             # Dash audio
251             '141', '172', '140', '171', '139',
252         ]
253
254         def format_info(f_id):
255             info = YoutubeIE._formats[f_id].copy()
256
257             # XXX: In real cases InfoExtractor._parse_mpd_formats() fills up 'acodec'
258             # and 'vcodec', while in tests such information is incomplete since
259             # commit a6c2c24479e5f4827ceb06f64d855329c0a6f593
260             # test_YoutubeDL.test_youtube_format_selection is broken without
261             # this fix
262             if 'acodec' in info and 'vcodec' not in info:
263                 info['vcodec'] = 'none'
264             elif 'vcodec' in info and 'acodec' not in info:
265                 info['acodec'] = 'none'
266
267             info['format_id'] = f_id
268             info['url'] = 'url:' + f_id
269             return info
270         formats_order = [format_info(f_id) for f_id in order]
271
272         info_dict = _make_result(list(formats_order), extractor='youtube')
273         ydl = YDL({'format': 'bestvideo+bestaudio'})
274         yie = YoutubeIE(ydl)
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'], '137+141')
279         self.assertEqual(downloaded['ext'], 'mp4')
280
281         info_dict = _make_result(list(formats_order), extractor='youtube')
282         ydl = YDL({'format': 'bestvideo[height>=999999]+bestaudio/best'})
283         yie = YoutubeIE(ydl)
284         yie._sort_formats(info_dict['formats'])
285         ydl.process_ie_result(info_dict)
286         downloaded = ydl.downloaded_info_dicts[0]
287         self.assertEqual(downloaded['format_id'], '38')
288
289         info_dict = _make_result(list(formats_order), extractor='youtube')
290         ydl = YDL({'format': 'bestvideo/best,bestaudio'})
291         yie = YoutubeIE(ydl)
292         yie._sort_formats(info_dict['formats'])
293         ydl.process_ie_result(info_dict)
294         downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
295         self.assertEqual(downloaded_ids, ['137', '141'])
296
297         info_dict = _make_result(list(formats_order), extractor='youtube')
298         ydl = YDL({'format': '(bestvideo[ext=mp4],bestvideo[ext=webm])+bestaudio'})
299         yie = YoutubeIE(ydl)
300         yie._sort_formats(info_dict['formats'])
301         ydl.process_ie_result(info_dict)
302         downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
303         self.assertEqual(downloaded_ids, ['137+141', '248+141'])
304
305         info_dict = _make_result(list(formats_order), extractor='youtube')
306         ydl = YDL({'format': '(bestvideo[ext=mp4],bestvideo[ext=webm])[height<=720]+bestaudio'})
307         yie = YoutubeIE(ydl)
308         yie._sort_formats(info_dict['formats'])
309         ydl.process_ie_result(info_dict)
310         downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
311         self.assertEqual(downloaded_ids, ['136+141', '247+141'])
312
313         info_dict = _make_result(list(formats_order), extractor='youtube')
314         ydl = YDL({'format': '(bestvideo[ext=none]/bestvideo[ext=webm])+bestaudio'})
315         yie = YoutubeIE(ydl)
316         yie._sort_formats(info_dict['formats'])
317         ydl.process_ie_result(info_dict)
318         downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
319         self.assertEqual(downloaded_ids, ['248+141'])
320
321         for f1, f2 in zip(formats_order, formats_order[1:]):
322             info_dict = _make_result([f1, f2], extractor='youtube')
323             ydl = YDL({'format': 'best/bestvideo'})
324             yie = YoutubeIE(ydl)
325             yie._sort_formats(info_dict['formats'])
326             ydl.process_ie_result(info_dict)
327             downloaded = ydl.downloaded_info_dicts[0]
328             self.assertEqual(downloaded['format_id'], f1['format_id'])
329
330             info_dict = _make_result([f2, f1], extractor='youtube')
331             ydl = YDL({'format': 'best/bestvideo'})
332             yie = YoutubeIE(ydl)
333             yie._sort_formats(info_dict['formats'])
334             ydl.process_ie_result(info_dict)
335             downloaded = ydl.downloaded_info_dicts[0]
336             self.assertEqual(downloaded['format_id'], f1['format_id'])
337
338     def test_audio_only_extractor_format_selection(self):
339         # For extractors with incomplete formats (all formats are audio-only or
340         # video-only) best and worst should fallback to corresponding best/worst
341         # video-only or audio-only formats (as per
342         # https://github.com/rg3/youtube-dl/pull/5556)
343         formats = [
344             {'format_id': 'low', 'ext': 'mp3', 'preference': 1, 'vcodec': 'none', 'url': TEST_URL},
345             {'format_id': 'high', 'ext': 'mp3', 'preference': 2, 'vcodec': 'none', 'url': TEST_URL},
346         ]
347         info_dict = _make_result(formats)
348
349         ydl = YDL({'format': 'best'})
350         ydl.process_ie_result(info_dict.copy())
351         downloaded = ydl.downloaded_info_dicts[0]
352         self.assertEqual(downloaded['format_id'], 'high')
353
354         ydl = YDL({'format': 'worst'})
355         ydl.process_ie_result(info_dict.copy())
356         downloaded = ydl.downloaded_info_dicts[0]
357         self.assertEqual(downloaded['format_id'], 'low')
358
359     def test_format_not_available(self):
360         formats = [
361             {'format_id': 'regular', 'ext': 'mp4', 'height': 360, 'url': TEST_URL},
362             {'format_id': 'video', 'ext': 'mp4', 'height': 720, 'acodec': 'none', 'url': TEST_URL},
363         ]
364         info_dict = _make_result(formats)
365
366         # This must fail since complete video-audio format does not match filter
367         # and extractor does not provide incomplete only formats (i.e. only
368         # video-only or audio-only).
369         ydl = YDL({'format': 'best[height>360]'})
370         self.assertRaises(ExtractorError, ydl.process_ie_result, info_dict.copy())
371
372     def test_invalid_format_specs(self):
373         def assert_syntax_error(format_spec):
374             ydl = YDL({'format': format_spec})
375             info_dict = _make_result([{'format_id': 'foo', 'url': TEST_URL}])
376             self.assertRaises(SyntaxError, ydl.process_ie_result, info_dict)
377
378         assert_syntax_error('bestvideo,,best')
379         assert_syntax_error('+bestaudio')
380         assert_syntax_error('bestvideo+')
381         assert_syntax_error('/')
382
383     def test_format_filtering(self):
384         formats = [
385             {'format_id': 'A', 'filesize': 500, 'width': 1000},
386             {'format_id': 'B', 'filesize': 1000, 'width': 500},
387             {'format_id': 'C', 'filesize': 1000, 'width': 400},
388             {'format_id': 'D', 'filesize': 2000, 'width': 600},
389             {'format_id': 'E', 'filesize': 3000},
390             {'format_id': 'F'},
391             {'format_id': 'G', 'filesize': 1000000},
392         ]
393         for f in formats:
394             f['url'] = 'http://_/'
395             f['ext'] = 'unknown'
396         info_dict = _make_result(formats)
397
398         ydl = YDL({'format': 'best[filesize<3000]'})
399         ydl.process_ie_result(info_dict)
400         downloaded = ydl.downloaded_info_dicts[0]
401         self.assertEqual(downloaded['format_id'], 'D')
402
403         ydl = YDL({'format': 'best[filesize<=3000]'})
404         ydl.process_ie_result(info_dict)
405         downloaded = ydl.downloaded_info_dicts[0]
406         self.assertEqual(downloaded['format_id'], 'E')
407
408         ydl = YDL({'format': 'best[filesize <= ? 3000]'})
409         ydl.process_ie_result(info_dict)
410         downloaded = ydl.downloaded_info_dicts[0]
411         self.assertEqual(downloaded['format_id'], 'F')
412
413         ydl = YDL({'format': 'best [filesize = 1000] [width>450]'})
414         ydl.process_ie_result(info_dict)
415         downloaded = ydl.downloaded_info_dicts[0]
416         self.assertEqual(downloaded['format_id'], 'B')
417
418         ydl = YDL({'format': 'best [filesize = 1000] [width!=450]'})
419         ydl.process_ie_result(info_dict)
420         downloaded = ydl.downloaded_info_dicts[0]
421         self.assertEqual(downloaded['format_id'], 'C')
422
423         ydl = YDL({'format': '[filesize>?1]'})
424         ydl.process_ie_result(info_dict)
425         downloaded = ydl.downloaded_info_dicts[0]
426         self.assertEqual(downloaded['format_id'], 'G')
427
428         ydl = YDL({'format': '[filesize<1M]'})
429         ydl.process_ie_result(info_dict)
430         downloaded = ydl.downloaded_info_dicts[0]
431         self.assertEqual(downloaded['format_id'], 'E')
432
433         ydl = YDL({'format': '[filesize<1MiB]'})
434         ydl.process_ie_result(info_dict)
435         downloaded = ydl.downloaded_info_dicts[0]
436         self.assertEqual(downloaded['format_id'], 'G')
437
438         ydl = YDL({'format': 'all[width>=400][width<=600]'})
439         ydl.process_ie_result(info_dict)
440         downloaded_ids = [info['format_id'] for info in ydl.downloaded_info_dicts]
441         self.assertEqual(downloaded_ids, ['B', 'C', 'D'])
442
443         ydl = YDL({'format': 'best[height<40]'})
444         try:
445             ydl.process_ie_result(info_dict)
446         except ExtractorError:
447             pass
448         self.assertEqual(ydl.downloaded_info_dicts, [])
449
450
451 class TestYoutubeDL(unittest.TestCase):
452     def test_subtitles(self):
453         def s_formats(lang, autocaption=False):
454             return [{
455                 'ext': ext,
456                 'url': 'http://localhost/video.%s.%s' % (lang, ext),
457                 '_auto': autocaption,
458             } for ext in ['vtt', 'srt', 'ass']]
459         subtitles = dict((l, s_formats(l)) for l in ['en', 'fr', 'es'])
460         auto_captions = dict((l, s_formats(l, True)) for l in ['it', 'pt', 'es'])
461         info_dict = {
462             'id': 'test',
463             'title': 'Test',
464             'url': 'http://localhost/video.mp4',
465             'subtitles': subtitles,
466             'automatic_captions': auto_captions,
467             'extractor': 'TEST',
468         }
469
470         def get_info(params={}):
471             params.setdefault('simulate', True)
472             ydl = YDL(params)
473             ydl.report_warning = lambda *args, **kargs: None
474             return ydl.process_video_result(info_dict, download=False)
475
476         result = get_info()
477         self.assertFalse(result.get('requested_subtitles'))
478         self.assertEqual(result['subtitles'], subtitles)
479         self.assertEqual(result['automatic_captions'], auto_captions)
480
481         result = get_info({'writesubtitles': True})
482         subs = result['requested_subtitles']
483         self.assertTrue(subs)
484         self.assertEqual(set(subs.keys()), set(['en']))
485         self.assertTrue(subs['en'].get('data') is None)
486         self.assertEqual(subs['en']['ext'], 'ass')
487
488         result = get_info({'writesubtitles': True, 'subtitlesformat': 'foo/srt'})
489         subs = result['requested_subtitles']
490         self.assertEqual(subs['en']['ext'], 'srt')
491
492         result = get_info({'writesubtitles': True, 'subtitleslangs': ['es', 'fr', 'it']})
493         subs = result['requested_subtitles']
494         self.assertTrue(subs)
495         self.assertEqual(set(subs.keys()), set(['es', 'fr']))
496
497         result = get_info({'writesubtitles': True, 'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
498         subs = result['requested_subtitles']
499         self.assertTrue(subs)
500         self.assertEqual(set(subs.keys()), set(['es', 'pt']))
501         self.assertFalse(subs['es']['_auto'])
502         self.assertTrue(subs['pt']['_auto'])
503
504         result = get_info({'writeautomaticsub': True, 'subtitleslangs': ['es', 'pt']})
505         subs = result['requested_subtitles']
506         self.assertTrue(subs)
507         self.assertEqual(set(subs.keys()), set(['es', 'pt']))
508         self.assertTrue(subs['es']['_auto'])
509         self.assertTrue(subs['pt']['_auto'])
510
511     def test_add_extra_info(self):
512         test_dict = {
513             'extractor': 'Foo',
514         }
515         extra_info = {
516             'extractor': 'Bar',
517             'playlist': 'funny videos',
518         }
519         YDL.add_extra_info(test_dict, extra_info)
520         self.assertEqual(test_dict['extractor'], 'Foo')
521         self.assertEqual(test_dict['playlist'], 'funny videos')
522
523     def test_prepare_filename(self):
524         info = {
525             'id': '1234',
526             'ext': 'mp4',
527             'width': None,
528         }
529
530         def fname(templ):
531             ydl = YoutubeDL({'outtmpl': templ})
532             return ydl.prepare_filename(info)
533         self.assertEqual(fname('%(id)s.%(ext)s'), '1234.mp4')
534         self.assertEqual(fname('%(id)s-%(width)s.%(ext)s'), '1234-NA.mp4')
535         # Replace missing fields with 'NA'
536         self.assertEqual(fname('%(uploader_date)s-%(id)s.%(ext)s'), 'NA-1234.mp4')
537
538     def test_format_note(self):
539         ydl = YoutubeDL()
540         self.assertEqual(ydl._format_note({}), '')
541         assertRegexpMatches(self, ydl._format_note({
542             'vbr': 10,
543         }), '^\s*10k$')
544         assertRegexpMatches(self, ydl._format_note({
545             'fps': 30,
546         }), '^30fps$')
547
548     def test_postprocessors(self):
549         filename = 'post-processor-testfile.mp4'
550         audiofile = filename + '.mp3'
551
552         class SimplePP(PostProcessor):
553             def run(self, info):
554                 with open(audiofile, 'wt') as f:
555                     f.write('EXAMPLE')
556                 return [info['filepath']], info
557
558         def run_pp(params, PP):
559             with open(filename, 'wt') as f:
560                 f.write('EXAMPLE')
561             ydl = YoutubeDL(params)
562             ydl.add_post_processor(PP())
563             ydl.post_process(filename, {'filepath': filename})
564
565         run_pp({'keepvideo': True}, SimplePP)
566         self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
567         self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
568         os.unlink(filename)
569         os.unlink(audiofile)
570
571         run_pp({'keepvideo': False}, SimplePP)
572         self.assertFalse(os.path.exists(filename), '%s exists' % filename)
573         self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile)
574         os.unlink(audiofile)
575
576         class ModifierPP(PostProcessor):
577             def run(self, info):
578                 with open(info['filepath'], 'wt') as f:
579                     f.write('MODIFIED')
580                 return [], info
581
582         run_pp({'keepvideo': False}, ModifierPP)
583         self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename)
584         os.unlink(filename)
585
586     def test_match_filter(self):
587         class FilterYDL(YDL):
588             def __init__(self, *args, **kwargs):
589                 super(FilterYDL, self).__init__(*args, **kwargs)
590                 self.params['simulate'] = True
591
592             def process_info(self, info_dict):
593                 super(YDL, self).process_info(info_dict)
594
595             def _match_entry(self, info_dict, incomplete):
596                 res = super(FilterYDL, self)._match_entry(info_dict, incomplete)
597                 if res is None:
598                     self.downloaded_info_dicts.append(info_dict)
599                 return res
600
601         first = {
602             'id': '1',
603             'url': TEST_URL,
604             'title': 'one',
605             'extractor': 'TEST',
606             'duration': 30,
607             'filesize': 10 * 1024,
608         }
609         second = {
610             'id': '2',
611             'url': TEST_URL,
612             'title': 'two',
613             'extractor': 'TEST',
614             'duration': 10,
615             'description': 'foo',
616             'filesize': 5 * 1024,
617         }
618         videos = [first, second]
619
620         def get_videos(filter_=None):
621             ydl = FilterYDL({'match_filter': filter_})
622             for v in videos:
623                 ydl.process_ie_result(v, download=True)
624             return [v['id'] for v in ydl.downloaded_info_dicts]
625
626         res = get_videos()
627         self.assertEqual(res, ['1', '2'])
628
629         def f(v):
630             if v['id'] == '1':
631                 return None
632             else:
633                 return 'Video id is not 1'
634         res = get_videos(f)
635         self.assertEqual(res, ['1'])
636
637         f = match_filter_func('duration < 30')
638         res = get_videos(f)
639         self.assertEqual(res, ['2'])
640
641         f = match_filter_func('description = foo')
642         res = get_videos(f)
643         self.assertEqual(res, ['2'])
644
645         f = match_filter_func('description =? foo')
646         res = get_videos(f)
647         self.assertEqual(res, ['1', '2'])
648
649         f = match_filter_func('filesize > 5KiB')
650         res = get_videos(f)
651         self.assertEqual(res, ['1'])
652
653     def test_playlist_items_selection(self):
654         entries = [{
655             'id': compat_str(i),
656             'title': compat_str(i),
657             'url': TEST_URL,
658         } for i in range(1, 5)]
659         playlist = {
660             '_type': 'playlist',
661             'id': 'test',
662             'entries': entries,
663             'extractor': 'test:playlist',
664             'extractor_key': 'test:playlist',
665             'webpage_url': 'http://example.com',
666         }
667
668         def get_ids(params):
669             ydl = YDL(params)
670             # make a copy because the dictionary can be modified
671             ydl.process_ie_result(playlist.copy())
672             return [int(v['id']) for v in ydl.downloaded_info_dicts]
673
674         result = get_ids({})
675         self.assertEqual(result, [1, 2, 3, 4])
676
677         result = get_ids({'playlistend': 10})
678         self.assertEqual(result, [1, 2, 3, 4])
679
680         result = get_ids({'playlistend': 2})
681         self.assertEqual(result, [1, 2])
682
683         result = get_ids({'playliststart': 10})
684         self.assertEqual(result, [])
685
686         result = get_ids({'playliststart': 2})
687         self.assertEqual(result, [2, 3, 4])
688
689         result = get_ids({'playlist_items': '2-4'})
690         self.assertEqual(result, [2, 3, 4])
691
692         result = get_ids({'playlist_items': '2,4'})
693         self.assertEqual(result, [2, 4])
694
695         result = get_ids({'playlist_items': '10'})
696         self.assertEqual(result, [])
697
698     def test_urlopen_no_file_protocol(self):
699         # see https://github.com/rg3/youtube-dl/issues/8227
700         ydl = YDL()
701         self.assertRaises(compat_urllib_error.URLError, ydl.urlopen, 'file:///etc/passwd')
702
703     def test_do_not_override_ie_key_in_url_transparent(self):
704         ydl = YDL()
705
706         class Foo1IE(InfoExtractor):
707             _VALID_URL = r'foo1:'
708
709             def _real_extract(self, url):
710                 return {
711                     '_type': 'url_transparent',
712                     'url': 'foo2:',
713                     'ie_key': 'Foo2',
714                 }
715
716         class Foo2IE(InfoExtractor):
717             _VALID_URL = r'foo2:'
718
719             def _real_extract(self, url):
720                 return {
721                     '_type': 'url',
722                     'url': 'foo3:',
723                     'ie_key': 'Foo3',
724                 }
725
726         class Foo3IE(InfoExtractor):
727             _VALID_URL = r'foo3:'
728
729             def _real_extract(self, url):
730                 return _make_result([{'url': TEST_URL}])
731
732         ydl.add_info_extractor(Foo1IE(ydl))
733         ydl.add_info_extractor(Foo2IE(ydl))
734         ydl.add_info_extractor(Foo3IE(ydl))
735         ydl.extract_info('foo1:')
736         downloaded = ydl.downloaded_info_dicts[0]
737         self.assertEqual(downloaded['url'], TEST_URL)
738
739
740 if __name__ == '__main__':
741     unittest.main()