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