You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

747 lines
28 KiB

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