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