Allow to specify multiple subtitles languages separated by commas (closes #518)
[youtube-dl] / youtube_dl / __init__.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 __authors__  = (
5     'Ricardo Garcia Gonzalez',
6     'Danny Colligan',
7     'Benjamin Johnson',
8     'Vasyl\' Vavrychuk',
9     'Witold Baryluk',
10     'Paweł Paprota',
11     'Gergely Imreh',
12     'Rogério Brito',
13     'Philipp Hagemeister',
14     'Sören Schulze',
15     'Kevin Ngo',
16     'Ori Avtalion',
17     'shizeeg',
18     'Filippo Valsorda',
19     'Christian Albrecht',
20     'Dave Vasilevsky',
21     'Jaime Marquínez Ferrándiz',
22     'Jeff Crouse',
23     'Osama Khalid',
24     'Michael Walter',
25     'M. Yasoob Ullah Khalid',
26     'Julien Fraichard',
27     'Johny Mo Swag',
28     'Axel Noack',
29     'Albert Kim',
30     'Pierre Rudloff',
31 )
32
33 __license__ = 'Public Domain'
34
35 import codecs
36 import getpass
37 import optparse
38 import os
39 import random
40 import re
41 import shlex
42 import socket
43 import subprocess
44 import sys
45 import warnings
46 import platform
47
48 from .utils import *
49 from .update import update_self
50 from .version import __version__
51 from .FileDownloader import *
52 from .extractor import gen_extractors
53 from .YoutubeDL import YoutubeDL
54 from .PostProcessor import *
55
56 def parseOpts(overrideArguments=None):
57     def _readOptions(filename_bytes):
58         try:
59             optionf = open(filename_bytes)
60         except IOError:
61             return [] # silently skip if file is not present
62         try:
63             res = []
64             for l in optionf:
65                 res += shlex.split(l, comments=True)
66         finally:
67             optionf.close()
68         return res
69
70     def _format_option_string(option):
71         ''' ('-o', '--option') -> -o, --format METAVAR'''
72
73         opts = []
74
75         if option._short_opts:
76             opts.append(option._short_opts[0])
77         if option._long_opts:
78             opts.append(option._long_opts[0])
79         if len(opts) > 1:
80             opts.insert(1, ', ')
81
82         if option.takes_value(): opts.append(' %s' % option.metavar)
83
84         return "".join(opts)
85
86     def _comma_separated_values_options_callback(option, opt_str, value, parser):
87         setattr(parser.values, option.dest, value.split(','))
88
89     def _find_term_columns():
90         columns = os.environ.get('COLUMNS', None)
91         if columns:
92             return int(columns)
93
94         try:
95             sp = subprocess.Popen(['stty', 'size'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
96             out,err = sp.communicate()
97             return int(out.split()[1])
98         except:
99             pass
100         return None
101
102     max_width = 80
103     max_help_position = 80
104
105     # No need to wrap help messages if we're on a wide console
106     columns = _find_term_columns()
107     if columns: max_width = columns
108
109     fmt = optparse.IndentedHelpFormatter(width=max_width, max_help_position=max_help_position)
110     fmt.format_option_strings = _format_option_string
111
112     kw = {
113         'version'   : __version__,
114         'formatter' : fmt,
115         'usage' : '%prog [options] url [url...]',
116         'conflict_handler' : 'resolve',
117     }
118
119     parser = optparse.OptionParser(**kw)
120
121     # option groups
122     general        = optparse.OptionGroup(parser, 'General Options')
123     selection      = optparse.OptionGroup(parser, 'Video Selection')
124     authentication = optparse.OptionGroup(parser, 'Authentication Options')
125     video_format   = optparse.OptionGroup(parser, 'Video Format Options')
126     subtitles      = optparse.OptionGroup(parser, 'Subtitle Options')
127     downloader     = optparse.OptionGroup(parser, 'Download Options')
128     postproc       = optparse.OptionGroup(parser, 'Post-processing Options')
129     filesystem     = optparse.OptionGroup(parser, 'Filesystem Options')
130     verbosity      = optparse.OptionGroup(parser, 'Verbosity / Simulation Options')
131
132     general.add_option('-h', '--help',
133             action='help', help='print this help text and exit')
134     general.add_option('-v', '--version',
135             action='version', help='print program version and exit')
136     general.add_option('-U', '--update',
137             action='store_true', dest='update_self', help='update this program to latest version. Make sure that you have sufficient permissions (run with sudo if needed)')
138     general.add_option('-i', '--ignore-errors',
139             action='store_true', dest='ignoreerrors', help='continue on download errors', default=False)
140     general.add_option('--dump-user-agent',
141             action='store_true', dest='dump_user_agent',
142             help='display the current browser identification', default=False)
143     general.add_option('--user-agent',
144             dest='user_agent', help='specify a custom user agent', metavar='UA')
145     general.add_option('--referer',
146             dest='referer', help='specify a custom referer, use if the video access is restricted to one domain',
147             metavar='REF', default=None)
148     general.add_option('--list-extractors',
149             action='store_true', dest='list_extractors',
150             help='List all supported extractors and the URLs they would handle', default=False)
151     general.add_option('--extractor-descriptions',
152             action='store_true', dest='list_extractor_descriptions',
153             help='Output descriptions of all supported extractors', default=False)
154     general.add_option('--proxy', dest='proxy', default=None, help='Use the specified HTTP/HTTPS proxy', metavar='URL')
155     general.add_option('--no-check-certificate', action='store_true', dest='no_check_certificate', default=False, help='Suppress HTTPS certificate validation.')
156
157
158     selection.add_option('--playlist-start',
159             dest='playliststart', metavar='NUMBER', help='playlist video to start at (default is %default)', default=1)
160     selection.add_option('--playlist-end',
161             dest='playlistend', metavar='NUMBER', help='playlist video to end at (default is last)', default=-1)
162     selection.add_option('--match-title', dest='matchtitle', metavar='REGEX',help='download only matching titles (regex or caseless sub-string)')
163     selection.add_option('--reject-title', dest='rejecttitle', metavar='REGEX',help='skip download for matching titles (regex or caseless sub-string)')
164     selection.add_option('--max-downloads', metavar='NUMBER', dest='max_downloads', help='Abort after downloading NUMBER files', default=None)
165     selection.add_option('--min-filesize', metavar='SIZE', dest='min_filesize', help="Do not download any videos smaller than SIZE (e.g. 50k or 44.6m)", default=None)
166     selection.add_option('--max-filesize', metavar='SIZE', dest='max_filesize', help="Do not download any videos larger than SIZE (e.g. 50k or 44.6m)", default=None)
167     selection.add_option('--date', metavar='DATE', dest='date', help='download only videos uploaded in this date', default=None)
168     selection.add_option('--datebefore', metavar='DATE', dest='datebefore', help='download only videos uploaded before this date', default=None)
169     selection.add_option('--dateafter', metavar='DATE', dest='dateafter', help='download only videos uploaded after this date', default=None)
170
171
172     authentication.add_option('-u', '--username',
173             dest='username', metavar='USERNAME', help='account username')
174     authentication.add_option('-p', '--password',
175             dest='password', metavar='PASSWORD', help='account password')
176     authentication.add_option('-n', '--netrc',
177             action='store_true', dest='usenetrc', help='use .netrc authentication data', default=False)
178     authentication.add_option('--video-password',
179             dest='videopassword', metavar='PASSWORD', help='video password (vimeo only)')
180
181
182     video_format.add_option('-f', '--format',
183             action='store', dest='format', metavar='FORMAT',
184             help='video format code, specifiy the order of preference using slashes: "-f 22/17/18"')
185     video_format.add_option('--all-formats',
186             action='store_const', dest='format', help='download all available video formats', const='all')
187     video_format.add_option('--prefer-free-formats',
188             action='store_true', dest='prefer_free_formats', default=False, help='prefer free video formats unless a specific one is requested')
189     video_format.add_option('--max-quality',
190             action='store', dest='format_limit', metavar='FORMAT', help='highest quality format to download')
191     video_format.add_option('-F', '--list-formats',
192             action='store_true', dest='listformats', help='list all available formats (currently youtube only)')
193
194     subtitles.add_option('--write-sub', '--write-srt',
195             action='store_true', dest='writesubtitles',
196             help='write subtitle file (currently youtube only)', default=False)
197     subtitles.add_option('--write-auto-sub', '--write-automatic-sub',
198             action='store_true', dest='writeautomaticsub',
199             help='write automatic subtitle file (currently youtube only)', default=False)
200     subtitles.add_option('--only-sub',
201             action='store_true', dest='skip_download',
202             help='[deprecated] alias of --skip-download', default=False)
203     subtitles.add_option('--all-subs',
204             action='store_true', dest='allsubtitles',
205             help='downloads all the available subtitles of the video', default=False)
206     subtitles.add_option('--list-subs',
207             action='store_true', dest='listsubtitles',
208             help='lists all available subtitles for the video', default=False)
209     subtitles.add_option('--sub-format',
210             action='store', dest='subtitlesformat', metavar='FORMAT',
211             help='subtitle format (default=srt) ([sbv/vtt] youtube only)', default='srt')
212     subtitles.add_option('--sub-lang', '--sub-langs', '--srt-lang',
213             action='callback', dest='subtitleslang', metavar='LANGS', type='str',
214             default=[], callback=_comma_separated_values_options_callback,
215             help='languages of the subtitles to download (optional) separated by commas, use IETF language tags like \'en,pt\'')
216
217     downloader.add_option('-r', '--rate-limit',
218             dest='ratelimit', metavar='LIMIT', help='maximum download rate (e.g. 50k or 44.6m)')
219     downloader.add_option('-R', '--retries',
220             dest='retries', metavar='RETRIES', help='number of retries (default is %default)', default=10)
221     downloader.add_option('--buffer-size',
222             dest='buffersize', metavar='SIZE', help='size of download buffer (e.g. 1024 or 16k) (default is %default)', default="1024")
223     downloader.add_option('--no-resize-buffer',
224             action='store_true', dest='noresizebuffer',
225             help='do not automatically adjust the buffer size. By default, the buffer size is automatically resized from an initial value of SIZE.', default=False)
226     downloader.add_option('--test', action='store_true', dest='test', default=False, help=optparse.SUPPRESS_HELP)
227
228     verbosity.add_option('-q', '--quiet',
229             action='store_true', dest='quiet', help='activates quiet mode', default=False)
230     verbosity.add_option('-s', '--simulate',
231             action='store_true', dest='simulate', help='do not download the video and do not write anything to disk', default=False)
232     verbosity.add_option('--skip-download',
233             action='store_true', dest='skip_download', help='do not download the video', default=False)
234     verbosity.add_option('-g', '--get-url',
235             action='store_true', dest='geturl', help='simulate, quiet but print URL', default=False)
236     verbosity.add_option('-e', '--get-title',
237             action='store_true', dest='gettitle', help='simulate, quiet but print title', default=False)
238     verbosity.add_option('--get-id',
239             action='store_true', dest='getid', help='simulate, quiet but print id', default=False)
240     verbosity.add_option('--get-thumbnail',
241             action='store_true', dest='getthumbnail',
242             help='simulate, quiet but print thumbnail URL', default=False)
243     verbosity.add_option('--get-description',
244             action='store_true', dest='getdescription',
245             help='simulate, quiet but print video description', default=False)
246     verbosity.add_option('--get-filename',
247             action='store_true', dest='getfilename',
248             help='simulate, quiet but print output filename', default=False)
249     verbosity.add_option('--get-format',
250             action='store_true', dest='getformat',
251             help='simulate, quiet but print output format', default=False)
252     verbosity.add_option('--newline',
253             action='store_true', dest='progress_with_newline', help='output progress bar as new lines', default=False)
254     verbosity.add_option('--no-progress',
255             action='store_true', dest='noprogress', help='do not print progress bar', default=False)
256     verbosity.add_option('--console-title',
257             action='store_true', dest='consoletitle',
258             help='display progress in console titlebar', default=False)
259     verbosity.add_option('-v', '--verbose',
260             action='store_true', dest='verbose', help='print various debugging information', default=False)
261     verbosity.add_option('--dump-intermediate-pages',
262             action='store_true', dest='dump_intermediate_pages', default=False,
263             help='print downloaded pages to debug problems(very verbose)')
264
265     filesystem.add_option('-t', '--title',
266             action='store_true', dest='usetitle', help='use title in file name (default)', default=False)
267     filesystem.add_option('--id',
268             action='store_true', dest='useid', help='use only video ID in file name', default=False)
269     filesystem.add_option('-l', '--literal',
270             action='store_true', dest='usetitle', help='[deprecated] alias of --title', default=False)
271     filesystem.add_option('-A', '--auto-number',
272             action='store_true', dest='autonumber',
273             help='number downloaded files starting from 00000', default=False)
274     filesystem.add_option('-o', '--output',
275             dest='outtmpl', metavar='TEMPLATE',
276             help=('output filename template. Use %(title)s to get the title, '
277                   '%(uploader)s for the uploader name, %(uploader_id)s for the uploader nickname if different, '
278                   '%(autonumber)s to get an automatically incremented number, '
279                   '%(ext)s for the filename extension, %(upload_date)s for the upload date (YYYYMMDD), '
280                   '%(extractor)s for the provider (youtube, metacafe, etc), '
281                   '%(id)s for the video id , %(playlist)s for the playlist the video is in, '
282                   '%(playlist_index)s for the position in the playlist and %% for a literal percent. '
283                   'Use - to output to stdout. Can also be used to download to a different directory, '
284                   'for example with -o \'/my/downloads/%(uploader)s/%(title)s-%(id)s.%(ext)s\' .'))
285     filesystem.add_option('--autonumber-size',
286             dest='autonumber_size', metavar='NUMBER',
287             help='Specifies the number of digits in %(autonumber)s when it is present in output filename template or --autonumber option is given')
288     filesystem.add_option('--restrict-filenames',
289             action='store_true', dest='restrictfilenames',
290             help='Restrict filenames to only ASCII characters, and avoid "&" and spaces in filenames', default=False)
291     filesystem.add_option('-a', '--batch-file',
292             dest='batchfile', metavar='FILE', help='file containing URLs to download (\'-\' for stdin)')
293     filesystem.add_option('-w', '--no-overwrites',
294             action='store_true', dest='nooverwrites', help='do not overwrite files', default=False)
295     filesystem.add_option('-c', '--continue',
296             action='store_true', dest='continue_dl', help='resume partially downloaded files', default=True)
297     filesystem.add_option('--no-continue',
298             action='store_false', dest='continue_dl',
299             help='do not resume partially downloaded files (restart from beginning)')
300     filesystem.add_option('--cookies',
301             dest='cookiefile', metavar='FILE', help='file to read cookies from and dump cookie jar in')
302     filesystem.add_option('--no-part',
303             action='store_true', dest='nopart', help='do not use .part files', default=False)
304     filesystem.add_option('--no-mtime',
305             action='store_false', dest='updatetime',
306             help='do not use the Last-modified header to set the file modification time', default=True)
307     filesystem.add_option('--write-description',
308             action='store_true', dest='writedescription',
309             help='write video description to a .description file', default=False)
310     filesystem.add_option('--write-info-json',
311             action='store_true', dest='writeinfojson',
312             help='write video metadata to a .info.json file', default=False)
313     filesystem.add_option('--write-thumbnail',
314             action='store_true', dest='writethumbnail',
315             help='write thumbnail image to disk', default=False)
316
317
318     postproc.add_option('-x', '--extract-audio', action='store_true', dest='extractaudio', default=False,
319             help='convert video files to audio-only files (requires ffmpeg or avconv and ffprobe or avprobe)')
320     postproc.add_option('--audio-format', metavar='FORMAT', dest='audioformat', default='best',
321             help='"best", "aac", "vorbis", "mp3", "m4a", "opus", or "wav"; best by default')
322     postproc.add_option('--audio-quality', metavar='QUALITY', dest='audioquality', default='5',
323             help='ffmpeg/avconv audio quality specification, insert a value between 0 (better) and 9 (worse) for VBR or a specific bitrate like 128K (default 5)')
324     postproc.add_option('--recode-video', metavar='FORMAT', dest='recodevideo', default=None,
325             help='Encode the video to another format if necessary (currently supported: mp4|flv|ogg|webm)')
326     postproc.add_option('-k', '--keep-video', action='store_true', dest='keepvideo', default=False,
327             help='keeps the video file on disk after the post-processing; the video is erased by default')
328     postproc.add_option('--no-post-overwrites', action='store_true', dest='nopostoverwrites', default=False,
329             help='do not overwrite post-processed files; the post-processed files are overwritten by default')
330     postproc.add_option('--embed-subs', action='store_true', dest='embedsubtitles', default=False,
331             help='embed subtitles in the video (only for mp4 videos)')
332
333
334     parser.add_option_group(general)
335     parser.add_option_group(selection)
336     parser.add_option_group(downloader)
337     parser.add_option_group(filesystem)
338     parser.add_option_group(verbosity)
339     parser.add_option_group(video_format)
340     parser.add_option_group(subtitles)
341     parser.add_option_group(authentication)
342     parser.add_option_group(postproc)
343
344     if overrideArguments is not None:
345         opts, args = parser.parse_args(overrideArguments)
346         if opts.verbose:
347             sys.stderr.write(u'[debug] Override config: ' + repr(overrideArguments) + '\n')
348     else:
349         xdg_config_home = os.environ.get('XDG_CONFIG_HOME')
350         if xdg_config_home:
351             userConfFile = os.path.join(xdg_config_home, 'youtube-dl.conf')
352         else:
353             userConfFile = os.path.join(os.path.expanduser('~'), '.config', 'youtube-dl.conf')
354         systemConf = _readOptions('/etc/youtube-dl.conf')
355         userConf = _readOptions(userConfFile)
356         commandLineConf = sys.argv[1:]
357         argv = systemConf + userConf + commandLineConf
358         opts, args = parser.parse_args(argv)
359         if opts.verbose:
360             sys.stderr.write(u'[debug] System config: ' + repr(systemConf) + '\n')
361             sys.stderr.write(u'[debug] User config: ' + repr(userConf) + '\n')
362             sys.stderr.write(u'[debug] Command-line args: ' + repr(commandLineConf) + '\n')
363
364     return parser, opts, args
365
366 def _real_main(argv=None):
367     # Compatibility fixes for Windows
368     if sys.platform == 'win32':
369         # https://github.com/rg3/youtube-dl/issues/820
370         codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else None)
371
372     parser, opts, args = parseOpts(argv)
373
374     # Open appropriate CookieJar
375     if opts.cookiefile is None:
376         jar = compat_cookiejar.CookieJar()
377     else:
378         try:
379             jar = compat_cookiejar.MozillaCookieJar(opts.cookiefile)
380             if os.access(opts.cookiefile, os.R_OK):
381                 jar.load()
382         except (IOError, OSError) as err:
383             if opts.verbose:
384                 traceback.print_exc()
385             sys.stderr.write(u'ERROR: unable to open cookie file\n')
386             sys.exit(101)
387     # Set user agent
388     if opts.user_agent is not None:
389         std_headers['User-Agent'] = opts.user_agent
390
391     # Set referer
392     if opts.referer is not None:
393         std_headers['Referer'] = opts.referer
394
395     # Dump user agent
396     if opts.dump_user_agent:
397         compat_print(std_headers['User-Agent'])
398         sys.exit(0)
399
400     # Batch file verification
401     batchurls = []
402     if opts.batchfile is not None:
403         try:
404             if opts.batchfile == '-':
405                 batchfd = sys.stdin
406             else:
407                 batchfd = open(opts.batchfile, 'r')
408             batchurls = batchfd.readlines()
409             batchurls = [x.strip() for x in batchurls]
410             batchurls = [x for x in batchurls if len(x) > 0 and not re.search(r'^[#/;]', x)]
411             if opts.verbose:
412                 sys.stderr.write(u'[debug] Batch file urls: ' + repr(batchurls) + u'\n')
413         except IOError:
414             sys.exit(u'ERROR: batch file could not be read')
415     all_urls = batchurls + args
416     all_urls = [url.strip() for url in all_urls]
417
418     # General configuration
419     cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar)
420     if opts.proxy is not None:
421         if opts.proxy == '':
422             proxies = {}
423         else:
424             proxies = {'http': opts.proxy, 'https': opts.proxy}
425     else:
426         proxies = compat_urllib_request.getproxies()
427         # Set HTTPS proxy to HTTP one if given (https://github.com/rg3/youtube-dl/issues/805)
428         if 'http' in proxies and 'https' not in proxies:
429             proxies['https'] = proxies['http']
430     proxy_handler = compat_urllib_request.ProxyHandler(proxies)
431     https_handler = make_HTTPS_handler(opts)
432     opener = compat_urllib_request.build_opener(https_handler, proxy_handler, cookie_processor, YoutubeDLHandler())
433     compat_urllib_request.install_opener(opener)
434     socket.setdefaulttimeout(300) # 5 minutes should be enough (famous last words)
435
436     extractors = gen_extractors()
437
438     if opts.list_extractors:
439         for ie in sorted(extractors, key=lambda ie: ie.IE_NAME.lower()):
440             compat_print(ie.IE_NAME + (' (CURRENTLY BROKEN)' if not ie._WORKING else ''))
441             matchedUrls = [url for url in all_urls if ie.suitable(url)]
442             all_urls = [url for url in all_urls if url not in matchedUrls]
443             for mu in matchedUrls:
444                 compat_print(u'  ' + mu)
445         sys.exit(0)
446     if opts.list_extractor_descriptions:
447         for ie in sorted(extractors, key=lambda ie: ie.IE_NAME.lower()):
448             if not ie._WORKING:
449                 continue
450             desc = getattr(ie, 'IE_DESC', ie.IE_NAME)
451             if hasattr(ie, 'SEARCH_KEY'):
452                 _SEARCHES = (u'cute kittens', u'slithering pythons', u'falling cat', u'angry poodle', u'purple fish', u'running tortoise')
453                 _COUNTS = (u'', u'5', u'10', u'all')
454                 desc += u' (Example: "%s%s:%s" )' % (ie.SEARCH_KEY, random.choice(_COUNTS), random.choice(_SEARCHES))
455             compat_print(desc)
456         sys.exit(0)
457
458
459     # Conflicting, missing and erroneous options
460     if opts.usenetrc and (opts.username is not None or opts.password is not None):
461         parser.error(u'using .netrc conflicts with giving username/password')
462     if opts.password is not None and opts.username is None:
463         parser.error(u' account username missing\n')
464     if opts.outtmpl is not None and (opts.usetitle or opts.autonumber or opts.useid):
465         parser.error(u'using output template conflicts with using title, video ID or auto number')
466     if opts.usetitle and opts.useid:
467         parser.error(u'using title conflicts with using video ID')
468     if opts.username is not None and opts.password is None:
469         opts.password = getpass.getpass(u'Type account password and press return:')
470     if opts.ratelimit is not None:
471         numeric_limit = FileDownloader.parse_bytes(opts.ratelimit)
472         if numeric_limit is None:
473             parser.error(u'invalid rate limit specified')
474         opts.ratelimit = numeric_limit
475     if opts.min_filesize is not None:
476         numeric_limit = FileDownloader.parse_bytes(opts.min_filesize)
477         if numeric_limit is None:
478             parser.error(u'invalid min_filesize specified')
479         opts.min_filesize = numeric_limit
480     if opts.max_filesize is not None:
481         numeric_limit = FileDownloader.parse_bytes(opts.max_filesize)
482         if numeric_limit is None:
483             parser.error(u'invalid max_filesize specified')
484         opts.max_filesize = numeric_limit
485     if opts.retries is not None:
486         try:
487             opts.retries = int(opts.retries)
488         except (TypeError, ValueError) as err:
489             parser.error(u'invalid retry count specified')
490     if opts.buffersize is not None:
491         numeric_buffersize = FileDownloader.parse_bytes(opts.buffersize)
492         if numeric_buffersize is None:
493             parser.error(u'invalid buffer size specified')
494         opts.buffersize = numeric_buffersize
495     try:
496         opts.playliststart = int(opts.playliststart)
497         if opts.playliststart <= 0:
498             raise ValueError(u'Playlist start must be positive')
499     except (TypeError, ValueError) as err:
500         parser.error(u'invalid playlist start number specified')
501     try:
502         opts.playlistend = int(opts.playlistend)
503         if opts.playlistend != -1 and (opts.playlistend <= 0 or opts.playlistend < opts.playliststart):
504             raise ValueError(u'Playlist end must be greater than playlist start')
505     except (TypeError, ValueError) as err:
506         parser.error(u'invalid playlist end number specified')
507     if opts.extractaudio:
508         if opts.audioformat not in ['best', 'aac', 'mp3', 'm4a', 'opus', 'vorbis', 'wav']:
509             parser.error(u'invalid audio format specified')
510     if opts.audioquality:
511         opts.audioquality = opts.audioquality.strip('k').strip('K')
512         if not opts.audioquality.isdigit():
513             parser.error(u'invalid audio quality specified')
514     if opts.recodevideo is not None:
515         if opts.recodevideo not in ['mp4', 'flv', 'webm', 'ogg']:
516             parser.error(u'invalid video recode format specified')
517     if opts.date is not None:
518         date = DateRange.day(opts.date)
519     else:
520         date = DateRange(opts.dateafter, opts.datebefore)
521
522     if sys.version_info < (3,):
523         # In Python 2, sys.argv is a bytestring (also note http://bugs.python.org/issue2128 for Windows systems)
524         if opts.outtmpl is not None:
525             opts.outtmpl = opts.outtmpl.decode(preferredencoding())
526     outtmpl =((opts.outtmpl is not None and opts.outtmpl)
527             or (opts.format == '-1' and opts.usetitle and u'%(title)s-%(id)s-%(format)s.%(ext)s')
528             or (opts.format == '-1' and u'%(id)s-%(format)s.%(ext)s')
529             or (opts.usetitle and opts.autonumber and u'%(autonumber)s-%(title)s-%(id)s.%(ext)s')
530             or (opts.usetitle and u'%(title)s-%(id)s.%(ext)s')
531             or (opts.useid and u'%(id)s.%(ext)s')
532             or (opts.autonumber and u'%(autonumber)s-%(id)s.%(ext)s')
533             or u'%(title)s-%(id)s.%(ext)s')
534
535     # YoutubeDL
536     ydl = YoutubeDL({
537         'usenetrc': opts.usenetrc,
538         'username': opts.username,
539         'password': opts.password,
540         'videopassword': opts.videopassword,
541         'quiet': (opts.quiet or opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
542         'forceurl': opts.geturl,
543         'forcetitle': opts.gettitle,
544         'forceid': opts.getid,
545         'forcethumbnail': opts.getthumbnail,
546         'forcedescription': opts.getdescription,
547         'forcefilename': opts.getfilename,
548         'forceformat': opts.getformat,
549         'simulate': opts.simulate,
550         'skip_download': (opts.skip_download or opts.simulate or opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
551         'format': opts.format,
552         'format_limit': opts.format_limit,
553         'listformats': opts.listformats,
554         'outtmpl': outtmpl,
555         'autonumber_size': opts.autonumber_size,
556         'restrictfilenames': opts.restrictfilenames,
557         'ignoreerrors': opts.ignoreerrors,
558         'ratelimit': opts.ratelimit,
559         'nooverwrites': opts.nooverwrites,
560         'retries': opts.retries,
561         'buffersize': opts.buffersize,
562         'noresizebuffer': opts.noresizebuffer,
563         'continuedl': opts.continue_dl,
564         'noprogress': opts.noprogress,
565         'progress_with_newline': opts.progress_with_newline,
566         'playliststart': opts.playliststart,
567         'playlistend': opts.playlistend,
568         'logtostderr': opts.outtmpl == '-',
569         'consoletitle': opts.consoletitle,
570         'nopart': opts.nopart,
571         'updatetime': opts.updatetime,
572         'writedescription': opts.writedescription,
573         'writeinfojson': opts.writeinfojson,
574         'writethumbnail': opts.writethumbnail,
575         'writesubtitles': opts.writesubtitles,
576         'writeautomaticsub': opts.writeautomaticsub,
577         'allsubtitles': opts.allsubtitles,
578         'listsubtitles': opts.listsubtitles,
579         'subtitlesformat': opts.subtitlesformat,
580         'subtitleslangs': opts.subtitleslang,
581         'matchtitle': decodeOption(opts.matchtitle),
582         'rejecttitle': decodeOption(opts.rejecttitle),
583         'max_downloads': opts.max_downloads,
584         'prefer_free_formats': opts.prefer_free_formats,
585         'verbose': opts.verbose,
586         'dump_intermediate_pages': opts.dump_intermediate_pages,
587         'test': opts.test,
588         'keepvideo': opts.keepvideo,
589         'min_filesize': opts.min_filesize,
590         'max_filesize': opts.max_filesize,
591         'daterange': date,
592         })
593
594     if opts.verbose:
595         sys.stderr.write(u'[debug] youtube-dl version ' + __version__ + u'\n')
596         try:
597             sp = subprocess.Popen(
598                 ['git', 'rev-parse', '--short', 'HEAD'],
599                 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
600                 cwd=os.path.dirname(os.path.abspath(__file__)))
601             out, err = sp.communicate()
602             out = out.decode().strip()
603             if re.match('[0-9a-f]+', out):
604                 sys.stderr.write(u'[debug] Git HEAD: ' + out + u'\n')
605         except:
606             try:
607                 sys.exc_clear()
608             except:
609                 pass
610         sys.stderr.write(u'[debug] Python version %s - %s' %(platform.python_version(), platform.platform()) + u'\n')
611         sys.stderr.write(u'[debug] Proxy map: ' + str(proxy_handler.proxies) + u'\n')
612
613     ydl.add_default_info_extractors()
614
615     # PostProcessors
616     if opts.extractaudio:
617         ydl.add_post_processor(FFmpegExtractAudioPP(preferredcodec=opts.audioformat, preferredquality=opts.audioquality, nopostoverwrites=opts.nopostoverwrites))
618     if opts.recodevideo:
619         ydl.add_post_processor(FFmpegVideoConvertor(preferedformat=opts.recodevideo))
620     if opts.embedsubtitles:
621         ydl.add_post_processor(FFmpegEmbedSubtitlePP(subtitlesformat=opts.subtitlesformat))
622
623     # Update version
624     if opts.update_self:
625         update_self(ydl.to_screen, opts.verbose, sys.argv[0])
626
627     # Maybe do nothing
628     if len(all_urls) < 1:
629         if not opts.update_self:
630             parser.error(u'you must provide at least one URL')
631         else:
632             sys.exit()
633
634     try:
635         retcode = ydl.download(all_urls)
636     except MaxDownloadsReached:
637         ydl.to_screen(u'--max-download limit reached, aborting.')
638         retcode = 101
639
640     # Dump cookie jar if requested
641     if opts.cookiefile is not None:
642         try:
643             jar.save()
644         except (IOError, OSError) as err:
645             sys.exit(u'ERROR: unable to save cookie jar')
646
647     sys.exit(retcode)
648
649 def main(argv=None):
650     try:
651         _real_main(argv)
652     except DownloadError:
653         sys.exit(1)
654     except SameFileError:
655         sys.exit(u'ERROR: fixed output name but more than one file to download')
656     except KeyboardInterrupt:
657         sys.exit(u'\nERROR: Interrupted by user')