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