2c2fd441cf40243e4be43273177b8c10f4374aaf
[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', default=False)
197     subtitles.add_option('--write-auto-sub', '--write-automatic-sub',
198             action='store_true', dest='writeautomaticsub',
199             help='write automatic subtitle file (youtube only)', default=False)
200     subtitles.add_option('--all-subs',
201             action='store_true', dest='allsubtitles',
202             help='downloads all the available subtitles of the video', default=False)
203     subtitles.add_option('--list-subs',
204             action='store_true', dest='listsubtitles',
205             help='lists all available subtitles for the video', default=False)
206     subtitles.add_option('--sub-format',
207             action='store', dest='subtitlesformat', metavar='FORMAT',
208             help='subtitle format (default=srt) ([sbv/vtt] youtube only)', default='srt')
209     subtitles.add_option('--sub-lang', '--sub-langs', '--srt-lang',
210             action='callback', dest='subtitleslangs', metavar='LANGS', type='str',
211             default=[], callback=_comma_separated_values_options_callback,
212             help='languages of the subtitles to download (optional) separated by commas, use IETF language tags like \'en,pt\'')
213
214     downloader.add_option('-r', '--rate-limit',
215             dest='ratelimit', metavar='LIMIT', help='maximum download rate (e.g. 50k or 44.6m)')
216     downloader.add_option('-R', '--retries',
217             dest='retries', metavar='RETRIES', help='number of retries (default is %default)', default=10)
218     downloader.add_option('--buffer-size',
219             dest='buffersize', metavar='SIZE', help='size of download buffer (e.g. 1024 or 16k) (default is %default)', default="1024")
220     downloader.add_option('--no-resize-buffer',
221             action='store_true', dest='noresizebuffer',
222             help='do not automatically adjust the buffer size. By default, the buffer size is automatically resized from an initial value of SIZE.', default=False)
223     downloader.add_option('--test', action='store_true', dest='test', default=False, help=optparse.SUPPRESS_HELP)
224
225     verbosity.add_option('-q', '--quiet',
226             action='store_true', dest='quiet', help='activates quiet mode', default=False)
227     verbosity.add_option('-s', '--simulate',
228             action='store_true', dest='simulate', help='do not download the video and do not write anything to disk', default=False)
229     verbosity.add_option('--skip-download',
230             action='store_true', dest='skip_download', help='do not download the video', default=False)
231     verbosity.add_option('-g', '--get-url',
232             action='store_true', dest='geturl', help='simulate, quiet but print URL', default=False)
233     verbosity.add_option('-e', '--get-title',
234             action='store_true', dest='gettitle', help='simulate, quiet but print title', default=False)
235     verbosity.add_option('--get-id',
236             action='store_true', dest='getid', help='simulate, quiet but print id', default=False)
237     verbosity.add_option('--get-thumbnail',
238             action='store_true', dest='getthumbnail',
239             help='simulate, quiet but print thumbnail URL', default=False)
240     verbosity.add_option('--get-description',
241             action='store_true', dest='getdescription',
242             help='simulate, quiet but print video description', default=False)
243     verbosity.add_option('--get-filename',
244             action='store_true', dest='getfilename',
245             help='simulate, quiet but print output filename', default=False)
246     verbosity.add_option('--get-format',
247             action='store_true', dest='getformat',
248             help='simulate, quiet but print output format', default=False)
249     verbosity.add_option('--newline',
250             action='store_true', dest='progress_with_newline', help='output progress bar as new lines', default=False)
251     verbosity.add_option('--no-progress',
252             action='store_true', dest='noprogress', help='do not print progress bar', default=False)
253     verbosity.add_option('--console-title',
254             action='store_true', dest='consoletitle',
255             help='display progress in console titlebar', default=False)
256     verbosity.add_option('-v', '--verbose',
257             action='store_true', dest='verbose', help='print various debugging information', default=False)
258     verbosity.add_option('--dump-intermediate-pages',
259             action='store_true', dest='dump_intermediate_pages', default=False,
260             help='print downloaded pages to debug problems(very verbose)')
261
262     filesystem.add_option('-t', '--title',
263             action='store_true', dest='usetitle', help='use title in file name (default)', default=False)
264     filesystem.add_option('--id',
265             action='store_true', dest='useid', help='use only video ID in file name', default=False)
266     filesystem.add_option('-l', '--literal',
267             action='store_true', dest='usetitle', help='[deprecated] alias of --title', default=False)
268     filesystem.add_option('-A', '--auto-number',
269             action='store_true', dest='autonumber',
270             help='number downloaded files starting from 00000', default=False)
271     filesystem.add_option('-o', '--output',
272             dest='outtmpl', metavar='TEMPLATE',
273             help=('output filename template. Use %(title)s to get the title, '
274                   '%(uploader)s for the uploader name, %(uploader_id)s for the uploader nickname if different, '
275                   '%(autonumber)s to get an automatically incremented number, '
276                   '%(ext)s for the filename extension, %(upload_date)s for the upload date (YYYYMMDD), '
277                   '%(extractor)s for the provider (youtube, metacafe, etc), '
278                   '%(id)s for the video id , %(playlist)s for the playlist the video is in, '
279                   '%(playlist_index)s for the position in the playlist and %% for a literal percent. '
280                   'Use - to output to stdout. Can also be used to download to a different directory, '
281                   'for example with -o \'/my/downloads/%(uploader)s/%(title)s-%(id)s.%(ext)s\' .'))
282     filesystem.add_option('--autonumber-size',
283             dest='autonumber_size', metavar='NUMBER',
284             help='Specifies the number of digits in %(autonumber)s when it is present in output filename template or --autonumber option is given')
285     filesystem.add_option('--restrict-filenames',
286             action='store_true', dest='restrictfilenames',
287             help='Restrict filenames to only ASCII characters, and avoid "&" and spaces in filenames', default=False)
288     filesystem.add_option('-a', '--batch-file',
289             dest='batchfile', metavar='FILE', help='file containing URLs to download (\'-\' for stdin)')
290     filesystem.add_option('-w', '--no-overwrites',
291             action='store_true', dest='nooverwrites', help='do not overwrite files', default=False)
292     filesystem.add_option('-c', '--continue',
293             action='store_true', dest='continue_dl', help='resume partially downloaded files', default=True)
294     filesystem.add_option('--no-continue',
295             action='store_false', dest='continue_dl',
296             help='do not resume partially downloaded files (restart from beginning)')
297     filesystem.add_option('--cookies',
298             dest='cookiefile', metavar='FILE', help='file to read cookies from and dump cookie jar in')
299     filesystem.add_option('--no-part',
300             action='store_true', dest='nopart', help='do not use .part files', default=False)
301     filesystem.add_option('--no-mtime',
302             action='store_false', dest='updatetime',
303             help='do not use the Last-modified header to set the file modification time', default=True)
304     filesystem.add_option('--write-description',
305             action='store_true', dest='writedescription',
306             help='write video description to a .description file', default=False)
307     filesystem.add_option('--write-info-json',
308             action='store_true', dest='writeinfojson',
309             help='write video metadata to a .info.json file', default=False)
310     filesystem.add_option('--write-thumbnail',
311             action='store_true', dest='writethumbnail',
312             help='write thumbnail image to disk', default=False)
313
314
315     postproc.add_option('-x', '--extract-audio', action='store_true', dest='extractaudio', default=False,
316             help='convert video files to audio-only files (requires ffmpeg or avconv and ffprobe or avprobe)')
317     postproc.add_option('--audio-format', metavar='FORMAT', dest='audioformat', default='best',
318             help='"best", "aac", "vorbis", "mp3", "m4a", "opus", or "wav"; best by default')
319     postproc.add_option('--audio-quality', metavar='QUALITY', dest='audioquality', default='5',
320             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)')
321     postproc.add_option('--recode-video', metavar='FORMAT', dest='recodevideo', default=None,
322             help='Encode the video to another format if necessary (currently supported: mp4|flv|ogg|webm)')
323     postproc.add_option('-k', '--keep-video', action='store_true', dest='keepvideo', default=False,
324             help='keeps the video file on disk after the post-processing; the video is erased by default')
325     postproc.add_option('--no-post-overwrites', action='store_true', dest='nopostoverwrites', default=False,
326             help='do not overwrite post-processed files; the post-processed files are overwritten by default')
327     postproc.add_option('--embed-subs', action='store_true', dest='embedsubtitles', default=False,
328             help='embed subtitles in the video (only for mp4 videos)')
329
330
331     parser.add_option_group(general)
332     parser.add_option_group(selection)
333     parser.add_option_group(downloader)
334     parser.add_option_group(filesystem)
335     parser.add_option_group(verbosity)
336     parser.add_option_group(video_format)
337     parser.add_option_group(subtitles)
338     parser.add_option_group(authentication)
339     parser.add_option_group(postproc)
340
341     if overrideArguments is not None:
342         opts, args = parser.parse_args(overrideArguments)
343         if opts.verbose:
344             sys.stderr.write(u'[debug] Override config: ' + repr(overrideArguments) + '\n')
345     else:
346         xdg_config_home = os.environ.get('XDG_CONFIG_HOME')
347         if xdg_config_home:
348             userConfFile = os.path.join(xdg_config_home, 'youtube-dl.conf')
349         else:
350             userConfFile = os.path.join(os.path.expanduser('~'), '.config', 'youtube-dl.conf')
351         systemConf = _readOptions('/etc/youtube-dl.conf')
352         userConf = _readOptions(userConfFile)
353         commandLineConf = sys.argv[1:]
354         argv = systemConf + userConf + commandLineConf
355         opts, args = parser.parse_args(argv)
356         if opts.verbose:
357             sys.stderr.write(u'[debug] System config: ' + repr(systemConf) + '\n')
358             sys.stderr.write(u'[debug] User config: ' + repr(userConf) + '\n')
359             sys.stderr.write(u'[debug] Command-line args: ' + repr(commandLineConf) + '\n')
360
361     return parser, opts, args
362
363 def _real_main(argv=None):
364     # Compatibility fixes for Windows
365     if sys.platform == 'win32':
366         # https://github.com/rg3/youtube-dl/issues/820
367         codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else None)
368
369     parser, opts, args = parseOpts(argv)
370
371     # Open appropriate CookieJar
372     if opts.cookiefile is None:
373         jar = compat_cookiejar.CookieJar()
374     else:
375         try:
376             jar = compat_cookiejar.MozillaCookieJar(opts.cookiefile)
377             if os.access(opts.cookiefile, os.R_OK):
378                 jar.load()
379         except (IOError, OSError) as err:
380             if opts.verbose:
381                 traceback.print_exc()
382             sys.stderr.write(u'ERROR: unable to open cookie file\n')
383             sys.exit(101)
384     # Set user agent
385     if opts.user_agent is not None:
386         std_headers['User-Agent'] = opts.user_agent
387
388     # Set referer
389     if opts.referer is not None:
390         std_headers['Referer'] = opts.referer
391
392     # Dump user agent
393     if opts.dump_user_agent:
394         compat_print(std_headers['User-Agent'])
395         sys.exit(0)
396
397     # Batch file verification
398     batchurls = []
399     if opts.batchfile is not None:
400         try:
401             if opts.batchfile == '-':
402                 batchfd = sys.stdin
403             else:
404                 batchfd = open(opts.batchfile, 'r')
405             batchurls = batchfd.readlines()
406             batchurls = [x.strip() for x in batchurls]
407             batchurls = [x for x in batchurls if len(x) > 0 and not re.search(r'^[#/;]', x)]
408             if opts.verbose:
409                 sys.stderr.write(u'[debug] Batch file urls: ' + repr(batchurls) + u'\n')
410         except IOError:
411             sys.exit(u'ERROR: batch file could not be read')
412     all_urls = batchurls + args
413     all_urls = [url.strip() for url in all_urls]
414
415     # General configuration
416     cookie_processor = compat_urllib_request.HTTPCookieProcessor(jar)
417     if opts.proxy is not None:
418         if opts.proxy == '':
419             proxies = {}
420         else:
421             proxies = {'http': opts.proxy, 'https': opts.proxy}
422     else:
423         proxies = compat_urllib_request.getproxies()
424         # Set HTTPS proxy to HTTP one if given (https://github.com/rg3/youtube-dl/issues/805)
425         if 'http' in proxies and 'https' not in proxies:
426             proxies['https'] = proxies['http']
427     proxy_handler = compat_urllib_request.ProxyHandler(proxies)
428     https_handler = make_HTTPS_handler(opts)
429     opener = compat_urllib_request.build_opener(https_handler, proxy_handler, cookie_processor, YoutubeDLHandler())
430     compat_urllib_request.install_opener(opener)
431     socket.setdefaulttimeout(300) # 5 minutes should be enough (famous last words)
432
433     extractors = gen_extractors()
434
435     if opts.list_extractors:
436         for ie in sorted(extractors, key=lambda ie: ie.IE_NAME.lower()):
437             compat_print(ie.IE_NAME + (' (CURRENTLY BROKEN)' if not ie._WORKING else ''))
438             matchedUrls = [url for url in all_urls if ie.suitable(url)]
439             all_urls = [url for url in all_urls if url not in matchedUrls]
440             for mu in matchedUrls:
441                 compat_print(u'  ' + mu)
442         sys.exit(0)
443     if opts.list_extractor_descriptions:
444         for ie in sorted(extractors, key=lambda ie: ie.IE_NAME.lower()):
445             if not ie._WORKING:
446                 continue
447             desc = getattr(ie, 'IE_DESC', ie.IE_NAME)
448             if hasattr(ie, 'SEARCH_KEY'):
449                 _SEARCHES = (u'cute kittens', u'slithering pythons', u'falling cat', u'angry poodle', u'purple fish', u'running tortoise')
450                 _COUNTS = (u'', u'5', u'10', u'all')
451                 desc += u' (Example: "%s%s:%s" )' % (ie.SEARCH_KEY, random.choice(_COUNTS), random.choice(_SEARCHES))
452             compat_print(desc)
453         sys.exit(0)
454
455
456     # Conflicting, missing and erroneous options
457     if opts.usenetrc and (opts.username is not None or opts.password is not None):
458         parser.error(u'using .netrc conflicts with giving username/password')
459     if opts.password is not None and opts.username is None:
460         parser.error(u' account username missing\n')
461     if opts.outtmpl is not None and (opts.usetitle or opts.autonumber or opts.useid):
462         parser.error(u'using output template conflicts with using title, video ID or auto number')
463     if opts.usetitle and opts.useid:
464         parser.error(u'using title conflicts with using video ID')
465     if opts.username is not None and opts.password is None:
466         opts.password = getpass.getpass(u'Type account password and press return:')
467     if opts.ratelimit is not None:
468         numeric_limit = FileDownloader.parse_bytes(opts.ratelimit)
469         if numeric_limit is None:
470             parser.error(u'invalid rate limit specified')
471         opts.ratelimit = numeric_limit
472     if opts.min_filesize is not None:
473         numeric_limit = FileDownloader.parse_bytes(opts.min_filesize)
474         if numeric_limit is None:
475             parser.error(u'invalid min_filesize specified')
476         opts.min_filesize = numeric_limit
477     if opts.max_filesize is not None:
478         numeric_limit = FileDownloader.parse_bytes(opts.max_filesize)
479         if numeric_limit is None:
480             parser.error(u'invalid max_filesize specified')
481         opts.max_filesize = numeric_limit
482     if opts.retries is not None:
483         try:
484             opts.retries = int(opts.retries)
485         except (TypeError, ValueError) as err:
486             parser.error(u'invalid retry count specified')
487     if opts.buffersize is not None:
488         numeric_buffersize = FileDownloader.parse_bytes(opts.buffersize)
489         if numeric_buffersize is None:
490             parser.error(u'invalid buffer size specified')
491         opts.buffersize = numeric_buffersize
492     try:
493         opts.playliststart = int(opts.playliststart)
494         if opts.playliststart <= 0:
495             raise ValueError(u'Playlist start must be positive')
496     except (TypeError, ValueError) as err:
497         parser.error(u'invalid playlist start number specified')
498     try:
499         opts.playlistend = int(opts.playlistend)
500         if opts.playlistend != -1 and (opts.playlistend <= 0 or opts.playlistend < opts.playliststart):
501             raise ValueError(u'Playlist end must be greater than playlist start')
502     except (TypeError, ValueError) as err:
503         parser.error(u'invalid playlist end number specified')
504     if opts.extractaudio:
505         if opts.audioformat not in ['best', 'aac', 'mp3', 'm4a', 'opus', 'vorbis', 'wav']:
506             parser.error(u'invalid audio format specified')
507     if opts.audioquality:
508         opts.audioquality = opts.audioquality.strip('k').strip('K')
509         if not opts.audioquality.isdigit():
510             parser.error(u'invalid audio quality specified')
511     if opts.recodevideo is not None:
512         if opts.recodevideo not in ['mp4', 'flv', 'webm', 'ogg']:
513             parser.error(u'invalid video recode format specified')
514     if opts.date is not None:
515         date = DateRange.day(opts.date)
516     else:
517         date = DateRange(opts.dateafter, opts.datebefore)
518
519     if sys.version_info < (3,):
520         # In Python 2, sys.argv is a bytestring (also note http://bugs.python.org/issue2128 for Windows systems)
521         if opts.outtmpl is not None:
522             opts.outtmpl = opts.outtmpl.decode(preferredencoding())
523     outtmpl =((opts.outtmpl is not None and opts.outtmpl)
524             or (opts.format == '-1' and opts.usetitle and u'%(title)s-%(id)s-%(format)s.%(ext)s')
525             or (opts.format == '-1' and u'%(id)s-%(format)s.%(ext)s')
526             or (opts.usetitle and opts.autonumber and u'%(autonumber)s-%(title)s-%(id)s.%(ext)s')
527             or (opts.usetitle and u'%(title)s-%(id)s.%(ext)s')
528             or (opts.useid and u'%(id)s.%(ext)s')
529             or (opts.autonumber and u'%(autonumber)s-%(id)s.%(ext)s')
530             or u'%(title)s-%(id)s.%(ext)s')
531
532     # YoutubeDL
533     ydl = YoutubeDL({
534         'usenetrc': opts.usenetrc,
535         'username': opts.username,
536         'password': opts.password,
537         'videopassword': opts.videopassword,
538         'quiet': (opts.quiet or opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
539         'forceurl': opts.geturl,
540         'forcetitle': opts.gettitle,
541         'forceid': opts.getid,
542         'forcethumbnail': opts.getthumbnail,
543         'forcedescription': opts.getdescription,
544         'forcefilename': opts.getfilename,
545         'forceformat': opts.getformat,
546         'simulate': opts.simulate,
547         '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),
548         'format': opts.format,
549         'format_limit': opts.format_limit,
550         'listformats': opts.listformats,
551         'outtmpl': outtmpl,
552         'autonumber_size': opts.autonumber_size,
553         'restrictfilenames': opts.restrictfilenames,
554         'ignoreerrors': opts.ignoreerrors,
555         'ratelimit': opts.ratelimit,
556         'nooverwrites': opts.nooverwrites,
557         'retries': opts.retries,
558         'buffersize': opts.buffersize,
559         'noresizebuffer': opts.noresizebuffer,
560         'continuedl': opts.continue_dl,
561         'noprogress': opts.noprogress,
562         'progress_with_newline': opts.progress_with_newline,
563         'playliststart': opts.playliststart,
564         'playlistend': opts.playlistend,
565         'logtostderr': opts.outtmpl == '-',
566         'consoletitle': opts.consoletitle,
567         'nopart': opts.nopart,
568         'updatetime': opts.updatetime,
569         'writedescription': opts.writedescription,
570         'writeinfojson': opts.writeinfojson,
571         'writethumbnail': opts.writethumbnail,
572         'writesubtitles': opts.writesubtitles,
573         'writeautomaticsub': opts.writeautomaticsub,
574         'allsubtitles': opts.allsubtitles,
575         'listsubtitles': opts.listsubtitles,
576         'subtitlesformat': opts.subtitlesformat,
577         'subtitleslangs': opts.subtitleslangs,
578         'matchtitle': decodeOption(opts.matchtitle),
579         'rejecttitle': decodeOption(opts.rejecttitle),
580         'max_downloads': opts.max_downloads,
581         'prefer_free_formats': opts.prefer_free_formats,
582         'verbose': opts.verbose,
583         'dump_intermediate_pages': opts.dump_intermediate_pages,
584         'test': opts.test,
585         'keepvideo': opts.keepvideo,
586         'min_filesize': opts.min_filesize,
587         'max_filesize': opts.max_filesize,
588         'daterange': date,
589         })
590
591     if opts.verbose:
592         sys.stderr.write(u'[debug] youtube-dl version ' + __version__ + u'\n')
593         try:
594             sp = subprocess.Popen(
595                 ['git', 'rev-parse', '--short', 'HEAD'],
596                 stdout=subprocess.PIPE, stderr=subprocess.PIPE,
597                 cwd=os.path.dirname(os.path.abspath(__file__)))
598             out, err = sp.communicate()
599             out = out.decode().strip()
600             if re.match('[0-9a-f]+', out):
601                 sys.stderr.write(u'[debug] Git HEAD: ' + out + u'\n')
602         except:
603             try:
604                 sys.exc_clear()
605             except:
606                 pass
607         sys.stderr.write(u'[debug] Python version %s - %s' %(platform.python_version(), platform.platform()) + u'\n')
608         sys.stderr.write(u'[debug] Proxy map: ' + str(proxy_handler.proxies) + u'\n')
609
610     ydl.add_default_info_extractors()
611
612     # PostProcessors
613     if opts.extractaudio:
614         ydl.add_post_processor(FFmpegExtractAudioPP(preferredcodec=opts.audioformat, preferredquality=opts.audioquality, nopostoverwrites=opts.nopostoverwrites))
615     if opts.recodevideo:
616         ydl.add_post_processor(FFmpegVideoConvertor(preferedformat=opts.recodevideo))
617     if opts.embedsubtitles:
618         ydl.add_post_processor(FFmpegEmbedSubtitlePP(subtitlesformat=opts.subtitlesformat))
619
620     # Update version
621     if opts.update_self:
622         update_self(ydl.to_screen, opts.verbose, sys.argv[0])
623
624     # Maybe do nothing
625     if len(all_urls) < 1:
626         if not opts.update_self:
627             parser.error(u'you must provide at least one URL')
628         else:
629             sys.exit()
630
631     try:
632         retcode = ydl.download(all_urls)
633     except MaxDownloadsReached:
634         ydl.to_screen(u'--max-download limit reached, aborting.')
635         retcode = 101
636
637     # Dump cookie jar if requested
638     if opts.cookiefile is not None:
639         try:
640             jar.save()
641         except (IOError, OSError) as err:
642             sys.exit(u'ERROR: unable to save cookie jar')
643
644     sys.exit(retcode)
645
646 def main(argv=None):
647     try:
648         _real_main(argv)
649     except DownloadError:
650         sys.exit(1)
651     except SameFileError:
652         sys.exit(u'ERROR: fixed output name but more than one file to download')
653     except KeyboardInterrupt:
654         sys.exit(u'\nERROR: Interrupted by user')