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