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.

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