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