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