Regroup and hide workaround options
[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     'Michael Orlitzky',
41     'Chris Gahan',
42     'Saimadhav Heblikar',
43     'Mike Col',
44     'Oleg Prutz',
45     'pulpe',
46     'Andreas Schmitz',
47     'Michael Kaiser',
48     'Niklas Laxström',
49     'David Triendl',
50     'Anthony Weems',
51     'David Wagner',
52     'Juan C. Olivares',
53     'Mattias Harrysson',
54     'phaer',
55     'Sainyam Kapoor',
56     'Nicolas Évrard',
57     'Jason Normore',
58     'Hoje Lee',
59     'Adam Thalhammer',
60     'Georg Jähnig',
61     'Ralf Haring',
62     'Koki Takahashi',
63     'Ariset Llerena',
64     'Adam Malcontenti-Wilson',
65     'Tobias Bell',
66     'Naglis Jonaitis',
67     'Charles Chen',
68     'Hassaan Ali',
69 )
70
71 __license__ = 'Public Domain'
72
73 import codecs
74 import io
75 import optparse
76 import os
77 import random
78 import shlex
79 import sys
80
81
82 from .utils import (
83     compat_getpass,
84     compat_print,
85     DateRange,
86     DEFAULT_OUTTMPL,
87     decodeOption,
88     get_term_width,
89     DownloadError,
90     get_cachedir,
91     MaxDownloadsReached,
92     preferredencoding,
93     read_batch_urls,
94     SameFileError,
95     setproctitle,
96     std_headers,
97     write_string,
98 )
99 from .update import update_self
100 from .downloader import (
101     FileDownloader,
102 )
103 from .extractor import gen_extractors
104 from .version import __version__
105 from .YoutubeDL import YoutubeDL
106 from .postprocessor import (
107     AtomicParsleyPP,
108     FFmpegAudioFixPP,
109     FFmpegMetadataPP,
110     FFmpegVideoConvertor,
111     FFmpegExtractAudioPP,
112     FFmpegEmbedSubtitlePP,
113     XAttrMetadataPP,
114 )
115
116
117 def parseOpts(overrideArguments=None):
118     def _readOptions(filename_bytes, default=[]):
119         try:
120             optionf = open(filename_bytes)
121         except IOError:
122             return default  # silently skip if file is not present
123         try:
124             res = []
125             for l in optionf:
126                 res += shlex.split(l, comments=True)
127         finally:
128             optionf.close()
129         return res
130
131     def _readUserConf():
132         xdg_config_home = os.environ.get('XDG_CONFIG_HOME')
133         if xdg_config_home:
134             userConfFile = os.path.join(xdg_config_home, 'youtube-dl', 'config')
135             if not os.path.isfile(userConfFile):
136                 userConfFile = os.path.join(xdg_config_home, 'youtube-dl.conf')
137         else:
138             userConfFile = os.path.join(os.path.expanduser('~'), '.config', 'youtube-dl', 'config')
139             if not os.path.isfile(userConfFile):
140                 userConfFile = os.path.join(os.path.expanduser('~'), '.config', 'youtube-dl.conf')
141         userConf = _readOptions(userConfFile, None)
142
143         if userConf is None:
144             appdata_dir = os.environ.get('appdata')
145             if appdata_dir:
146                 userConf = _readOptions(
147                     os.path.join(appdata_dir, 'youtube-dl', 'config'),
148                     default=None)
149                 if userConf is None:
150                     userConf = _readOptions(
151                         os.path.join(appdata_dir, 'youtube-dl', 'config.txt'),
152                         default=None)
153
154         if userConf is None:
155             userConf = _readOptions(
156                 os.path.join(os.path.expanduser('~'), 'youtube-dl.conf'),
157                 default=None)
158         if userConf is None:
159             userConf = _readOptions(
160                 os.path.join(os.path.expanduser('~'), 'youtube-dl.conf.txt'),
161                 default=None)
162
163         if userConf is None:
164             userConf = []
165
166         return userConf
167
168     def _format_option_string(option):
169         ''' ('-o', '--option') -> -o, --format METAVAR'''
170
171         opts = []
172
173         if option._short_opts:
174             opts.append(option._short_opts[0])
175         if option._long_opts:
176             opts.append(option._long_opts[0])
177         if len(opts) > 1:
178             opts.insert(1, ', ')
179
180         if option.takes_value(): opts.append(' %s' % option.metavar)
181
182         return "".join(opts)
183
184     def _comma_separated_values_options_callback(option, opt_str, value, parser):
185         setattr(parser.values, option.dest, value.split(','))
186
187     def _hide_login_info(opts):
188         opts = list(opts)
189         for private_opt in ['-p', '--password', '-u', '--username', '--video-password']:
190             try:
191                 i = opts.index(private_opt)
192                 opts[i+1] = '<PRIVATE>'
193             except ValueError:
194                 pass
195         return opts
196
197     max_width = 80
198     max_help_position = 80
199
200     # No need to wrap help messages if we're on a wide console
201     columns = get_term_width()
202     if columns: max_width = columns
203
204     fmt = optparse.IndentedHelpFormatter(width=max_width, max_help_position=max_help_position)
205     fmt.format_option_strings = _format_option_string
206
207     kw = {
208         'version'   : __version__,
209         'formatter' : fmt,
210         'usage' : '%prog [options] url [url...]',
211         'conflict_handler' : 'resolve',
212     }
213
214     parser = optparse.OptionParser(**kw)
215
216     # option groups
217     general        = optparse.OptionGroup(parser, 'General Options')
218     selection      = optparse.OptionGroup(parser, 'Video Selection')
219     authentication = optparse.OptionGroup(parser, 'Authentication Options')
220     video_format   = optparse.OptionGroup(parser, 'Video Format Options')
221     subtitles      = optparse.OptionGroup(parser, 'Subtitle Options')
222     downloader     = optparse.OptionGroup(parser, 'Download Options')
223     postproc       = optparse.OptionGroup(parser, 'Post-processing Options')
224     filesystem     = optparse.OptionGroup(parser, 'Filesystem Options')
225     workarounds    = optparse.OptionGroup(parser, 'Workarounds')
226     verbosity      = optparse.OptionGroup(parser, 'Verbosity / Simulation Options')
227
228     general.add_option('-h', '--help',
229             action='help', help='print this help text and exit')
230     general.add_option('-v', '--version',
231             action='version', help='print program version and exit')
232     general.add_option('-U', '--update',
233             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)')
234     general.add_option('-i', '--ignore-errors',
235             action='store_true', dest='ignoreerrors', help='continue on download errors, for example to skip unavailable videos in a playlist', default=False)
236     general.add_option('--abort-on-error',
237             action='store_false', dest='ignoreerrors',
238             help='Abort downloading of further videos (in the playlist or the command line) if an error occurs')
239     general.add_option('--dump-user-agent',
240             action='store_true', dest='dump_user_agent',
241             help='display the current browser identification', default=False)
242     general.add_option('--list-extractors',
243             action='store_true', dest='list_extractors',
244             help='List all supported extractors and the URLs they would handle', default=False)
245     general.add_option('--extractor-descriptions',
246             action='store_true', dest='list_extractor_descriptions',
247             help='Output descriptions of all supported extractors', default=False)
248     general.add_option(
249         '--proxy', dest='proxy', default=None, metavar='URL',
250         help='Use the specified HTTP/HTTPS proxy. Pass in an empty string (--proxy "") for direct connection')
251     general.add_option(
252         '--cache-dir', dest='cachedir', default=get_cachedir(), metavar='DIR',
253         help='Location in the filesystem where youtube-dl can store some downloaded information permanently. By default $XDG_CACHE_HOME/youtube-dl or ~/.cache/youtube-dl . At the moment, only YouTube player files (for videos with obfuscated signatures) are cached, but that may change.')
254     general.add_option(
255         '--no-cache-dir', action='store_const', const=None, dest='cachedir',
256         help='Disable filesystem caching')
257     general.add_option(
258         '--socket-timeout', dest='socket_timeout',
259         type=float, default=None, help=u'Time to wait before giving up, in seconds')
260     general.add_option(
261         '--bidi-workaround', dest='bidi_workaround', action='store_true',
262         help=u'Work around terminals that lack bidirectional text support. Requires bidiv or fribidi executable in PATH')
263     general.add_option(
264         '--default-search',
265         dest='default_search', metavar='PREFIX',
266         help='Use this prefix for unqualified URLs. For example "gvsearch2:" downloads two videos from google videos for  youtube-dl "large apple". Use the value "auto" to let youtube-dl guess. The default value "error" just throws an error.')
267     general.add_option(
268         '--ignore-config',
269         action='store_true',
270         help='Do not read configuration files. When given in the global configuration file /etc/youtube-dl.conf: do not read the user configuration in ~/.config/youtube-dl.conf (%APPDATA%/youtube-dl/config.txt on Windows)')
271
272     selection.add_option(
273         '--playlist-start',
274         dest='playliststart', metavar='NUMBER', default=1, type=int,
275         help='playlist video to start at (default is %default)')
276     selection.add_option(
277         '--playlist-end',
278         dest='playlistend', metavar='NUMBER', default=None, type=int,
279         help='playlist video to end at (default is last)')
280     selection.add_option('--match-title', dest='matchtitle', metavar='REGEX',help='download only matching titles (regex or caseless sub-string)')
281     selection.add_option('--reject-title', dest='rejecttitle', metavar='REGEX',help='skip download for matching titles (regex or caseless sub-string)')
282     selection.add_option('--max-downloads', metavar='NUMBER',
283                          dest='max_downloads', type=int, default=None,
284                          help='Abort after downloading NUMBER files')
285     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)
286     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)
287     selection.add_option('--date', metavar='DATE', dest='date', help='download only videos uploaded in this date', default=None)
288     selection.add_option(
289         '--datebefore', metavar='DATE', dest='datebefore', default=None,
290         help='download only videos uploaded on or before this date (i.e. inclusive)')
291     selection.add_option(
292         '--dateafter', metavar='DATE', dest='dateafter', default=None,
293         help='download only videos uploaded on or after this date (i.e. inclusive)')
294     selection.add_option(
295         '--min-views', metavar='COUNT', dest='min_views',
296         default=None, type=int,
297         help="Do not download any videos with less than COUNT views",)
298     selection.add_option(
299         '--max-views', metavar='COUNT', dest='max_views',
300         default=None, type=int,
301         help="Do not download any videos with more than COUNT views",)
302     selection.add_option('--no-playlist', action='store_true', dest='noplaylist', help='download only the currently playing video', default=False)
303     selection.add_option('--age-limit', metavar='YEARS', dest='age_limit',
304                          help='download only videos suitable for the given age',
305                          default=None, type=int)
306     selection.add_option('--download-archive', metavar='FILE',
307                          dest='download_archive',
308                          help='Download only videos not listed in the archive file. Record the IDs of all downloaded videos in it.')
309     selection.add_option(
310         '--include-ads', dest='include_ads',
311         action='store_true',
312         help='Download advertisements as well (experimental)')
313     selection.add_option(
314         '--youtube-include-dash-manifest', action='store_true',
315         dest='youtube_include_dash_manifest', default=False,
316         help='Try to download the DASH manifest on YouTube videos (experimental)')
317
318     authentication.add_option('-u', '--username',
319             dest='username', metavar='USERNAME', help='account username')
320     authentication.add_option('-p', '--password',
321             dest='password', metavar='PASSWORD', help='account password')
322     authentication.add_option('-n', '--netrc',
323             action='store_true', dest='usenetrc', help='use .netrc authentication data', default=False)
324     authentication.add_option('--video-password',
325             dest='videopassword', metavar='PASSWORD', help='video password (vimeo, smotri)')
326
327
328     video_format.add_option('-f', '--format',
329             action='store', dest='format', metavar='FORMAT', default=None,
330             help='video format code, specify the order of preference using slashes: "-f 22/17/18". "-f mp4" and "-f flv" are also supported. You can also use the special names "best", "bestvideo", "bestaudio", "worst", "worstvideo" and "worstaudio". By default, youtube-dl will pick the best quality.')
331     video_format.add_option('--all-formats',
332             action='store_const', dest='format', help='download all available video formats', const='all')
333     video_format.add_option('--prefer-free-formats',
334             action='store_true', dest='prefer_free_formats', default=False, help='prefer free video formats unless a specific one is requested')
335     video_format.add_option('--max-quality',
336             action='store', dest='format_limit', metavar='FORMAT', help='highest quality format to download')
337     video_format.add_option('-F', '--list-formats',
338             action='store_true', dest='listformats', help='list all available formats')
339
340     subtitles.add_option('--write-sub', '--write-srt',
341             action='store_true', dest='writesubtitles',
342             help='write subtitle file', default=False)
343     subtitles.add_option('--write-auto-sub', '--write-automatic-sub',
344             action='store_true', dest='writeautomaticsub',
345             help='write automatic subtitle file (youtube only)', default=False)
346     subtitles.add_option('--all-subs',
347             action='store_true', dest='allsubtitles',
348             help='downloads all the available subtitles of the video', default=False)
349     subtitles.add_option('--list-subs',
350             action='store_true', dest='listsubtitles',
351             help='lists all available subtitles for the video', default=False)
352     subtitles.add_option('--sub-format',
353             action='store', dest='subtitlesformat', metavar='FORMAT',
354             help='subtitle format (default=srt) ([sbv/vtt] youtube only)', default='srt')
355     subtitles.add_option('--sub-lang', '--sub-langs', '--srt-lang',
356             action='callback', dest='subtitleslangs', metavar='LANGS', type='str',
357             default=[], callback=_comma_separated_values_options_callback,
358             help='languages of the subtitles to download (optional) separated by commas, use IETF language tags like \'en,pt\'')
359
360     downloader.add_option('-r', '--rate-limit',
361             dest='ratelimit', metavar='LIMIT', help='maximum download rate in bytes per second (e.g. 50K or 4.2M)')
362     downloader.add_option('-R', '--retries',
363             dest='retries', metavar='RETRIES', help='number of retries (default is %default)', default=10)
364     downloader.add_option('--buffer-size',
365             dest='buffersize', metavar='SIZE', help='size of download buffer (e.g. 1024 or 16K) (default is %default)', default="1024")
366     downloader.add_option('--no-resize-buffer',
367             action='store_true', dest='noresizebuffer',
368             help='do not automatically adjust the buffer size. By default, the buffer size is automatically resized from an initial value of SIZE.', default=False)
369     downloader.add_option('--test', action='store_true', dest='test', default=False, help=optparse.SUPPRESS_HELP)
370
371     workarounds.add_option(
372         '--encoding', dest='encoding', metavar='ENCODING',
373         help='Force the specified encoding (experimental)')
374     workarounds.add_option(
375         '--no-check-certificate', action='store_true',
376         dest='no_check_certificate', default=False,
377         help='Suppress HTTPS certificate validation.')
378     workarounds.add_option(
379         '--prefer-insecure', '--prefer-unsecure', action='store_true', dest='prefer_insecure',
380         help='Use an unencrypted connection to retrieve information about the video. (Currently supported only for YouTube)')
381     workarounds.add_option(
382         '--user-agent', metavar='UA',
383         dest='user_agent', help='specify a custom user agent')
384     workarounds.add_option(
385         '--referer', metavar='REF',
386         dest='referer', default=None,
387         help='specify a custom referer, use if the video access is restricted to one domain',
388     )
389     workarounds.add_option(
390         '--add-header', metavar='FIELD:VALUE',
391         dest='headers', action='append',
392         help='specify a custom HTTP header and its value, separated by a colon \':\'. You can use this option multiple times',
393     )
394
395     verbosity.add_option('-q', '--quiet',
396             action='store_true', dest='quiet', help='activates quiet mode', default=False)
397     verbosity.add_option(
398         '--no-warnings',
399         dest='no_warnings', action='store_true', default=False,
400         help='Ignore warnings')
401     verbosity.add_option('-s', '--simulate',
402             action='store_true', dest='simulate', help='do not download the video and do not write anything to disk', default=False)
403     verbosity.add_option('--skip-download',
404             action='store_true', dest='skip_download', help='do not download the video', default=False)
405     verbosity.add_option('-g', '--get-url',
406             action='store_true', dest='geturl', help='simulate, quiet but print URL', default=False)
407     verbosity.add_option('-e', '--get-title',
408             action='store_true', dest='gettitle', help='simulate, quiet but print title', default=False)
409     verbosity.add_option('--get-id',
410             action='store_true', dest='getid', help='simulate, quiet but print id', default=False)
411     verbosity.add_option('--get-thumbnail',
412             action='store_true', dest='getthumbnail',
413             help='simulate, quiet but print thumbnail URL', default=False)
414     verbosity.add_option('--get-description',
415             action='store_true', dest='getdescription',
416             help='simulate, quiet but print video description', default=False)
417     verbosity.add_option('--get-duration',
418             action='store_true', dest='getduration',
419             help='simulate, quiet but print video length', default=False)
420     verbosity.add_option('--get-filename',
421             action='store_true', dest='getfilename',
422             help='simulate, quiet but print output filename', default=False)
423     verbosity.add_option('--get-format',
424             action='store_true', dest='getformat',
425             help='simulate, quiet but print output format', default=False)
426     verbosity.add_option('-j', '--dump-json',
427             action='store_true', dest='dumpjson',
428             help='simulate, quiet but print JSON information. See --output for a description of available keys.', default=False)
429     verbosity.add_option('--newline',
430             action='store_true', dest='progress_with_newline', help='output progress bar as new lines', default=False)
431     verbosity.add_option('--no-progress',
432             action='store_true', dest='noprogress', help='do not print progress bar', default=False)
433     verbosity.add_option('--console-title',
434             action='store_true', dest='consoletitle',
435             help='display progress in console titlebar', default=False)
436     verbosity.add_option('-v', '--verbose',
437             action='store_true', dest='verbose', help='print various debugging information', default=False)
438     verbosity.add_option('--dump-intermediate-pages',
439             action='store_true', dest='dump_intermediate_pages', default=False,
440             help='print downloaded pages to debug problems (very verbose)')
441     verbosity.add_option('--write-pages',
442             action='store_true', dest='write_pages', default=False,
443             help='Write downloaded intermediary pages to files in the current directory to debug problems')
444     verbosity.add_option('--youtube-print-sig-code',
445             action='store_true', dest='youtube_print_sig_code', default=False,
446             help=optparse.SUPPRESS_HELP)
447     verbosity.add_option('--print-traffic',
448             dest='debug_printtraffic', action='store_true', default=False,
449             help='Display sent and read HTTP traffic')
450
451
452     filesystem.add_option('-t', '--title',
453             action='store_true', dest='usetitle', help='use title in file name (default)', default=False)
454     filesystem.add_option('--id',
455             action='store_true', dest='useid', help='use only video ID in file name', default=False)
456     filesystem.add_option('-l', '--literal',
457             action='store_true', dest='usetitle', help='[deprecated] alias of --title', default=False)
458     filesystem.add_option('-A', '--auto-number',
459             action='store_true', dest='autonumber',
460             help='number downloaded files starting from 00000', default=False)
461     filesystem.add_option('-o', '--output',
462             dest='outtmpl', metavar='TEMPLATE',
463             help=('output filename template. Use %(title)s to get the title, '
464                   '%(uploader)s for the uploader name, %(uploader_id)s for the uploader nickname if different, '
465                   '%(autonumber)s to get an automatically incremented number, '
466                   '%(ext)s for the filename extension, '
467                   '%(format)s for the format description (like "22 - 1280x720" or "HD"), '
468                   '%(format_id)s for the unique id of the format (like Youtube\'s itags: "137"), '
469                   '%(upload_date)s for the upload date (YYYYMMDD), '
470                   '%(extractor)s for the provider (youtube, metacafe, etc), '
471                   '%(id)s for the video id, %(playlist)s for the playlist the video is in, '
472                   '%(playlist_index)s for the position in the playlist and %% for a literal percent. '
473                   '%(height)s and %(width)s for the width and height of the video format. '
474                   '%(resolution)s for a textual description of the resolution of the video format. '
475                   'Use - to output to stdout. Can also be used to download to a different directory, '
476                   'for example with -o \'/my/downloads/%(uploader)s/%(title)s-%(id)s.%(ext)s\' .'))
477     filesystem.add_option('--autonumber-size',
478             dest='autonumber_size', metavar='NUMBER',
479             help='Specifies the number of digits in %(autonumber)s when it is present in output filename template or --auto-number option is given')
480     filesystem.add_option('--restrict-filenames',
481             action='store_true', dest='restrictfilenames',
482             help='Restrict filenames to only ASCII characters, and avoid "&" and spaces in filenames', default=False)
483     filesystem.add_option('-a', '--batch-file',
484             dest='batchfile', metavar='FILE', help='file containing URLs to download (\'-\' for stdin)')
485     filesystem.add_option('--load-info',
486             dest='load_info_filename', metavar='FILE',
487             help='json file containing the video information (created with the "--write-json" option)')
488     filesystem.add_option('-w', '--no-overwrites',
489             action='store_true', dest='nooverwrites', help='do not overwrite files', default=False)
490     filesystem.add_option('-c', '--continue',
491             action='store_true', dest='continue_dl', help='force resume of partially downloaded files. By default, youtube-dl will resume downloads if possible.', default=True)
492     filesystem.add_option('--no-continue',
493             action='store_false', dest='continue_dl',
494             help='do not resume partially downloaded files (restart from beginning)')
495     filesystem.add_option('--cookies',
496             dest='cookiefile', metavar='FILE', help='file to read cookies from and dump cookie jar in')
497     filesystem.add_option('--no-part',
498             action='store_true', dest='nopart', help='do not use .part files', default=False)
499     filesystem.add_option('--no-mtime',
500             action='store_false', dest='updatetime',
501             help='do not use the Last-modified header to set the file modification time', default=True)
502     filesystem.add_option('--write-description',
503             action='store_true', dest='writedescription',
504             help='write video description to a .description file', default=False)
505     filesystem.add_option('--write-info-json',
506             action='store_true', dest='writeinfojson',
507             help='write video metadata to a .info.json file', default=False)
508     filesystem.add_option('--write-annotations',
509             action='store_true', dest='writeannotations',
510             help='write video annotations to a .annotation file', default=False)
511     filesystem.add_option('--write-thumbnail',
512             action='store_true', dest='writethumbnail',
513             help='write thumbnail image to disk', default=False)
514
515
516     postproc.add_option('-x', '--extract-audio', action='store_true', dest='extractaudio', default=False,
517             help='convert video files to audio-only files (requires ffmpeg or avconv and ffprobe or avprobe)')
518     postproc.add_option('--audio-format', metavar='FORMAT', dest='audioformat', default='best',
519             help='"best", "aac", "vorbis", "mp3", "m4a", "opus", or "wav"; best by default')
520     postproc.add_option('--audio-quality', metavar='QUALITY', dest='audioquality', default='5',
521             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)')
522     postproc.add_option('--recode-video', metavar='FORMAT', dest='recodevideo', default=None,
523             help='Encode the video to another format if necessary (currently supported: mp4|flv|ogg|webm|mkv)')
524     postproc.add_option('-k', '--keep-video', action='store_true', dest='keepvideo', default=False,
525             help='keeps the video file on disk after the post-processing; the video is erased by default')
526     postproc.add_option('--no-post-overwrites', action='store_true', dest='nopostoverwrites', default=False,
527             help='do not overwrite post-processed files; the post-processed files are overwritten by default')
528     postproc.add_option('--embed-subs', action='store_true', dest='embedsubtitles', default=False,
529             help='embed subtitles in the video (only for mp4 videos)')
530     postproc.add_option('--embed-thumbnail', action='store_true', dest='embedthumbnail', default=False,
531             help='embed thumbnail in the audio as cover art')
532     postproc.add_option('--add-metadata', action='store_true', dest='addmetadata', default=False,
533             help='write metadata to the video file')
534     postproc.add_option('--xattrs', action='store_true', dest='xattrs', default=False,
535             help='write metadata to the video file\'s xattrs (using dublin core and xdg standards)')
536     postproc.add_option('--prefer-avconv', action='store_false', dest='prefer_ffmpeg',
537         help='Prefer avconv over ffmpeg for running the postprocessors (default)')
538     postproc.add_option('--prefer-ffmpeg', action='store_true', dest='prefer_ffmpeg',
539         help='Prefer ffmpeg over avconv for running the postprocessors')
540
541
542     parser.add_option_group(general)
543     parser.add_option_group(selection)
544     parser.add_option_group(downloader)
545     parser.add_option_group(filesystem)
546     parser.add_option_group(verbosity)
547     parser.add_option_group(workarounds)
548     parser.add_option_group(video_format)
549     parser.add_option_group(subtitles)
550     parser.add_option_group(authentication)
551     parser.add_option_group(postproc)
552
553     if overrideArguments is not None:
554         opts, args = parser.parse_args(overrideArguments)
555         if opts.verbose:
556             write_string(u'[debug] Override config: ' + repr(overrideArguments) + '\n')
557     else:
558         commandLineConf = sys.argv[1:]
559         if '--ignore-config' in commandLineConf:
560             systemConf = []
561             userConf = []
562         else:
563             systemConf = _readOptions('/etc/youtube-dl.conf')
564             if '--ignore-config' in systemConf:
565                 userConf = []
566             else:
567                 userConf = _readUserConf()
568         argv = systemConf + userConf + commandLineConf
569
570         opts, args = parser.parse_args(argv)
571         if opts.verbose:
572             write_string(u'[debug] System config: ' + repr(_hide_login_info(systemConf)) + '\n')
573             write_string(u'[debug] User config: ' + repr(_hide_login_info(userConf)) + '\n')
574             write_string(u'[debug] Command-line args: ' + repr(_hide_login_info(commandLineConf)) + '\n')
575
576     return parser, opts, args
577
578
579 def _real_main(argv=None):
580     # Compatibility fixes for Windows
581     if sys.platform == 'win32':
582         # https://github.com/rg3/youtube-dl/issues/820
583         codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else None)
584
585     setproctitle(u'youtube-dl')
586
587     parser, opts, args = parseOpts(argv)
588
589     # Set user agent
590     if opts.user_agent is not None:
591         std_headers['User-Agent'] = opts.user_agent
592
593     # Set referer
594     if opts.referer is not None:
595         std_headers['Referer'] = opts.referer
596
597     # Custom HTTP headers
598     if opts.headers is not None:
599         for h in opts.headers:
600             if h.find(':', 1) < 0:
601                 parser.error(u'wrong header formatting, it should be key:value, not "%s"'%h)
602             key, value = h.split(':', 2)
603             if opts.verbose:
604                 write_string(u'[debug] Adding header from command line option %s:%s\n'%(key, value))
605             std_headers[key] = value
606
607     # Dump user agent
608     if opts.dump_user_agent:
609         compat_print(std_headers['User-Agent'])
610         sys.exit(0)
611
612     # Batch file verification
613     batch_urls = []
614     if opts.batchfile is not None:
615         try:
616             if opts.batchfile == '-':
617                 batchfd = sys.stdin
618             else:
619                 batchfd = io.open(opts.batchfile, 'r', encoding='utf-8', errors='ignore')
620             batch_urls = read_batch_urls(batchfd)
621             if opts.verbose:
622                 write_string(u'[debug] Batch file urls: ' + repr(batch_urls) + u'\n')
623         except IOError:
624             sys.exit(u'ERROR: batch file could not be read')
625     all_urls = batch_urls + args
626     all_urls = [url.strip() for url in all_urls]
627     _enc = preferredencoding()
628     all_urls = [url.decode(_enc, 'ignore') if isinstance(url, bytes) else url for url in all_urls]
629
630     extractors = gen_extractors()
631
632     if opts.list_extractors:
633         for ie in sorted(extractors, key=lambda ie: ie.IE_NAME.lower()):
634             compat_print(ie.IE_NAME + (' (CURRENTLY BROKEN)' if not ie._WORKING else ''))
635             matchedUrls = [url for url in all_urls if ie.suitable(url)]
636             for mu in matchedUrls:
637                 compat_print(u'  ' + mu)
638         sys.exit(0)
639     if opts.list_extractor_descriptions:
640         for ie in sorted(extractors, key=lambda ie: ie.IE_NAME.lower()):
641             if not ie._WORKING:
642                 continue
643             desc = getattr(ie, 'IE_DESC', ie.IE_NAME)
644             if desc is False:
645                 continue
646             if hasattr(ie, 'SEARCH_KEY'):
647                 _SEARCHES = (u'cute kittens', u'slithering pythons', u'falling cat', u'angry poodle', u'purple fish', u'running tortoise', u'sleeping bunny')
648                 _COUNTS = (u'', u'5', u'10', u'all')
649                 desc += u' (Example: "%s%s:%s" )' % (ie.SEARCH_KEY, random.choice(_COUNTS), random.choice(_SEARCHES))
650             compat_print(desc)
651         sys.exit(0)
652
653
654     # Conflicting, missing and erroneous options
655     if opts.usenetrc and (opts.username is not None or opts.password is not None):
656         parser.error(u'using .netrc conflicts with giving username/password')
657     if opts.password is not None and opts.username is None:
658         parser.error(u'account username missing\n')
659     if opts.outtmpl is not None and (opts.usetitle or opts.autonumber or opts.useid):
660         parser.error(u'using output template conflicts with using title, video ID or auto number')
661     if opts.usetitle and opts.useid:
662         parser.error(u'using title conflicts with using video ID')
663     if opts.username is not None and opts.password is None:
664         opts.password = compat_getpass(u'Type account password and press [Return]: ')
665     if opts.ratelimit is not None:
666         numeric_limit = FileDownloader.parse_bytes(opts.ratelimit)
667         if numeric_limit is None:
668             parser.error(u'invalid rate limit specified')
669         opts.ratelimit = numeric_limit
670     if opts.min_filesize is not None:
671         numeric_limit = FileDownloader.parse_bytes(opts.min_filesize)
672         if numeric_limit is None:
673             parser.error(u'invalid min_filesize specified')
674         opts.min_filesize = numeric_limit
675     if opts.max_filesize is not None:
676         numeric_limit = FileDownloader.parse_bytes(opts.max_filesize)
677         if numeric_limit is None:
678             parser.error(u'invalid max_filesize specified')
679         opts.max_filesize = numeric_limit
680     if opts.retries is not None:
681         try:
682             opts.retries = int(opts.retries)
683         except (TypeError, ValueError):
684             parser.error(u'invalid retry count specified')
685     if opts.buffersize is not None:
686         numeric_buffersize = FileDownloader.parse_bytes(opts.buffersize)
687         if numeric_buffersize is None:
688             parser.error(u'invalid buffer size specified')
689         opts.buffersize = numeric_buffersize
690     if opts.playliststart <= 0:
691         raise ValueError(u'Playlist start must be positive')
692     if opts.playlistend not in (-1, None) and opts.playlistend < opts.playliststart:
693         raise ValueError(u'Playlist end must be greater than playlist start')
694     if opts.extractaudio:
695         if opts.audioformat not in ['best', 'aac', 'mp3', 'm4a', 'opus', 'vorbis', 'wav']:
696             parser.error(u'invalid audio format specified')
697     if opts.audioquality:
698         opts.audioquality = opts.audioquality.strip('k').strip('K')
699         if not opts.audioquality.isdigit():
700             parser.error(u'invalid audio quality specified')
701     if opts.recodevideo is not None:
702         if opts.recodevideo not in ['mp4', 'flv', 'webm', 'ogg', 'mkv']:
703             parser.error(u'invalid video recode format specified')
704     if opts.date is not None:
705         date = DateRange.day(opts.date)
706     else:
707         date = DateRange(opts.dateafter, opts.datebefore)
708     if opts.default_search not in ('auto', 'auto_warning', None) and ':' not in opts.default_search:
709         parser.error(u'--default-search invalid; did you forget a colon (:) at the end?')
710
711     # Do not download videos when there are audio-only formats
712     if opts.extractaudio and not opts.keepvideo and opts.format is None:
713         opts.format = 'bestaudio/best'
714
715     # --all-sub automatically sets --write-sub if --write-auto-sub is not given
716     # this was the old behaviour if only --all-sub was given.
717     if opts.allsubtitles and (opts.writeautomaticsub == False):
718         opts.writesubtitles = True
719
720     if sys.version_info < (3,):
721         # In Python 2, sys.argv is a bytestring (also note http://bugs.python.org/issue2128 for Windows systems)
722         if opts.outtmpl is not None:
723             opts.outtmpl = opts.outtmpl.decode(preferredencoding())
724     outtmpl =((opts.outtmpl is not None and opts.outtmpl)
725             or (opts.format == '-1' and opts.usetitle and u'%(title)s-%(id)s-%(format)s.%(ext)s')
726             or (opts.format == '-1' and u'%(id)s-%(format)s.%(ext)s')
727             or (opts.usetitle and opts.autonumber and u'%(autonumber)s-%(title)s-%(id)s.%(ext)s')
728             or (opts.usetitle and u'%(title)s-%(id)s.%(ext)s')
729             or (opts.useid and u'%(id)s.%(ext)s')
730             or (opts.autonumber and u'%(autonumber)s-%(id)s.%(ext)s')
731             or DEFAULT_OUTTMPL)
732     if not os.path.splitext(outtmpl)[1] and opts.extractaudio:
733         parser.error(u'Cannot download a video and extract audio into the same'
734                      u' file! Use "{0}.%(ext)s" instead of "{0}" as the output'
735                      u' template'.format(outtmpl))
736
737     any_printing = opts.geturl or opts.gettitle or opts.getid or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat or opts.getduration or opts.dumpjson
738     download_archive_fn = os.path.expanduser(opts.download_archive) if opts.download_archive is not None else opts.download_archive
739
740     ydl_opts = {
741         'usenetrc': opts.usenetrc,
742         'username': opts.username,
743         'password': opts.password,
744         'videopassword': opts.videopassword,
745         'quiet': (opts.quiet or any_printing),
746         'no_warnings': opts.no_warnings,
747         'forceurl': opts.geturl,
748         'forcetitle': opts.gettitle,
749         'forceid': opts.getid,
750         'forcethumbnail': opts.getthumbnail,
751         'forcedescription': opts.getdescription,
752         'forceduration': opts.getduration,
753         'forcefilename': opts.getfilename,
754         'forceformat': opts.getformat,
755         'forcejson': opts.dumpjson,
756         'simulate': opts.simulate,
757         'skip_download': (opts.skip_download or opts.simulate or any_printing),
758         'format': opts.format,
759         'format_limit': opts.format_limit,
760         'listformats': opts.listformats,
761         'outtmpl': outtmpl,
762         'autonumber_size': opts.autonumber_size,
763         'restrictfilenames': opts.restrictfilenames,
764         'ignoreerrors': opts.ignoreerrors,
765         'ratelimit': opts.ratelimit,
766         'nooverwrites': opts.nooverwrites,
767         'retries': opts.retries,
768         'buffersize': opts.buffersize,
769         'noresizebuffer': opts.noresizebuffer,
770         'continuedl': opts.continue_dl,
771         'noprogress': opts.noprogress,
772         'progress_with_newline': opts.progress_with_newline,
773         'playliststart': opts.playliststart,
774         'playlistend': opts.playlistend,
775         'noplaylist': opts.noplaylist,
776         'logtostderr': opts.outtmpl == '-',
777         'consoletitle': opts.consoletitle,
778         'nopart': opts.nopart,
779         'updatetime': opts.updatetime,
780         'writedescription': opts.writedescription,
781         'writeannotations': opts.writeannotations,
782         'writeinfojson': opts.writeinfojson,
783         'writethumbnail': opts.writethumbnail,
784         'writesubtitles': opts.writesubtitles,
785         'writeautomaticsub': opts.writeautomaticsub,
786         'allsubtitles': opts.allsubtitles,
787         'listsubtitles': opts.listsubtitles,
788         'subtitlesformat': opts.subtitlesformat,
789         'subtitleslangs': opts.subtitleslangs,
790         'matchtitle': decodeOption(opts.matchtitle),
791         'rejecttitle': decodeOption(opts.rejecttitle),
792         'max_downloads': opts.max_downloads,
793         'prefer_free_formats': opts.prefer_free_formats,
794         'verbose': opts.verbose,
795         'dump_intermediate_pages': opts.dump_intermediate_pages,
796         'write_pages': opts.write_pages,
797         'test': opts.test,
798         'keepvideo': opts.keepvideo,
799         'min_filesize': opts.min_filesize,
800         'max_filesize': opts.max_filesize,
801         'min_views': opts.min_views,
802         'max_views': opts.max_views,
803         'daterange': date,
804         'cachedir': opts.cachedir,
805         'youtube_print_sig_code': opts.youtube_print_sig_code,
806         'age_limit': opts.age_limit,
807         'download_archive': download_archive_fn,
808         'cookiefile': opts.cookiefile,
809         'nocheckcertificate': opts.no_check_certificate,
810         'prefer_insecure': opts.prefer_insecure,
811         'proxy': opts.proxy,
812         'socket_timeout': opts.socket_timeout,
813         'bidi_workaround': opts.bidi_workaround,
814         'debug_printtraffic': opts.debug_printtraffic,
815         'prefer_ffmpeg': opts.prefer_ffmpeg,
816         'include_ads': opts.include_ads,
817         'default_search': opts.default_search,
818         'youtube_include_dash_manifest': opts.youtube_include_dash_manifest,
819         'encoding': opts.encoding,
820     }
821
822     with YoutubeDL(ydl_opts) as ydl:
823         ydl.print_debug_header()
824         ydl.add_default_info_extractors()
825
826         # PostProcessors
827         # Add the metadata pp first, the other pps will copy it
828         if opts.addmetadata:
829             ydl.add_post_processor(FFmpegMetadataPP())
830         if opts.extractaudio:
831             ydl.add_post_processor(FFmpegExtractAudioPP(preferredcodec=opts.audioformat, preferredquality=opts.audioquality, nopostoverwrites=opts.nopostoverwrites))
832         if opts.recodevideo:
833             ydl.add_post_processor(FFmpegVideoConvertor(preferedformat=opts.recodevideo))
834         if opts.embedsubtitles:
835             ydl.add_post_processor(FFmpegEmbedSubtitlePP(subtitlesformat=opts.subtitlesformat))
836         if opts.xattrs:
837             ydl.add_post_processor(XAttrMetadataPP())
838         if opts.embedthumbnail:
839             if not opts.addmetadata:
840                 ydl.add_post_processor(FFmpegAudioFixPP())
841             ydl.add_post_processor(AtomicParsleyPP())
842
843         # Update version
844         if opts.update_self:
845             update_self(ydl.to_screen, opts.verbose)
846
847         # Maybe do nothing
848         if (len(all_urls) < 1) and (opts.load_info_filename is None):
849             if not opts.update_self:
850                 parser.error(u'you must provide at least one URL')
851             else:
852                 sys.exit()
853
854         try:
855             if opts.load_info_filename is not None:
856                 retcode = ydl.download_with_info_file(opts.load_info_filename)
857             else:
858                 retcode = ydl.download(all_urls)
859         except MaxDownloadsReached:
860             ydl.to_screen(u'--max-download limit reached, aborting.')
861             retcode = 101
862
863     sys.exit(retcode)
864
865
866 def main(argv=None):
867     try:
868         _real_main(argv)
869     except DownloadError:
870         sys.exit(1)
871     except SameFileError:
872         sys.exit(u'ERROR: fixed output name but more than one file to download')
873     except KeyboardInterrupt:
874         sys.exit(u'\nERROR: Interrupted by user')