Merge pull request #2461 from niebles/master
[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 io
57 import locale
58 import optparse
59 import os
60 import random
61 import re
62 import shlex
63 import sys
64
65
66 from .utils import (
67     compat_print,
68     DateRange,
69     decodeOption,
70     get_term_width,
71     DownloadError,
72     get_cachedir,
73     MaxDownloadsReached,
74     preferredencoding,
75     read_batch_urls,
76     SameFileError,
77     setproctitle,
78     std_headers,
79     write_string,
80 )
81 from .update import update_self
82 from .FileDownloader import (
83     FileDownloader,
84 )
85 from .extractor import gen_extractors
86 from .version import __version__
87 from .YoutubeDL import YoutubeDL
88 from .postprocessor import (
89     FFmpegMetadataPP,
90     FFmpegVideoConvertor,
91     FFmpegExtractAudioPP,
92     FFmpegEmbedSubtitlePP,
93     XAttrMetadataPP,
94 )
95
96
97 def parseOpts(overrideArguments=None):
98     def _readOptions(filename_bytes, default=[]):
99         try:
100             optionf = open(filename_bytes)
101         except IOError:
102             return default  # silently skip if file is not present
103         try:
104             res = []
105             for l in optionf:
106                 res += shlex.split(l, comments=True)
107         finally:
108             optionf.close()
109         return res
110
111     def _readUserConf():
112         xdg_config_home = os.environ.get('XDG_CONFIG_HOME')
113         if xdg_config_home:
114             userConfFile = os.path.join(xdg_config_home, 'youtube-dl', 'config')
115             if not os.path.isfile(userConfFile):
116                 userConfFile = os.path.join(xdg_config_home, 'youtube-dl.conf')
117         else:
118             userConfFile = os.path.join(os.path.expanduser('~'), '.config', 'youtube-dl', 'config')
119             if not os.path.isfile(userConfFile):
120                 userConfFile = os.path.join(os.path.expanduser('~'), '.config', 'youtube-dl.conf')
121         userConf = _readOptions(userConfFile, None)
122
123         if userConf is None:
124             appdata_dir = os.environ.get('appdata')
125             if appdata_dir:
126                 userConf = _readOptions(
127                     os.path.join(appdata_dir, 'youtube-dl', 'config'),
128                     default=None)
129                 if userConf is None:
130                     userConf = _readOptions(
131                         os.path.join(appdata_dir, 'youtube-dl', 'config.txt'),
132                         default=None)
133
134         if userConf is None:
135             userConf = _readOptions(
136                 os.path.join(os.path.expanduser('~'), 'youtube-dl.conf'),
137                 default=None)
138         if userConf is None:
139             userConf = _readOptions(
140                 os.path.join(os.path.expanduser('~'), 'youtube-dl.conf.txt'),
141                 default=None)
142
143         if userConf is None:
144             userConf = []
145
146         return userConf
147
148     def _format_option_string(option):
149         ''' ('-o', '--option') -> -o, --format METAVAR'''
150
151         opts = []
152
153         if option._short_opts:
154             opts.append(option._short_opts[0])
155         if option._long_opts:
156             opts.append(option._long_opts[0])
157         if len(opts) > 1:
158             opts.insert(1, ', ')
159
160         if option.takes_value(): opts.append(' %s' % option.metavar)
161
162         return "".join(opts)
163
164     def _comma_separated_values_options_callback(option, opt_str, value, parser):
165         setattr(parser.values, option.dest, value.split(','))
166
167     def _hide_login_info(opts):
168         opts = list(opts)
169         for private_opt in ['-p', '--password', '-u', '--username', '--video-password']:
170             try:
171                 i = opts.index(private_opt)
172                 opts[i+1] = '<PRIVATE>'
173             except ValueError:
174                 pass
175         return opts
176
177     max_width = 80
178     max_help_position = 80
179
180     # No need to wrap help messages if we're on a wide console
181     columns = get_term_width()
182     if columns: max_width = columns
183
184     fmt = optparse.IndentedHelpFormatter(width=max_width, max_help_position=max_help_position)
185     fmt.format_option_strings = _format_option_string
186
187     kw = {
188         'version'   : __version__,
189         'formatter' : fmt,
190         'usage' : '%prog [options] url [url...]',
191         'conflict_handler' : 'resolve',
192     }
193
194     parser = optparse.OptionParser(**kw)
195
196     # option groups
197     general        = optparse.OptionGroup(parser, 'General Options')
198     selection      = optparse.OptionGroup(parser, 'Video Selection')
199     authentication = optparse.OptionGroup(parser, 'Authentication Options')
200     video_format   = optparse.OptionGroup(parser, 'Video Format Options')
201     subtitles      = optparse.OptionGroup(parser, 'Subtitle Options')
202     downloader     = optparse.OptionGroup(parser, 'Download Options')
203     postproc       = optparse.OptionGroup(parser, 'Post-processing Options')
204     filesystem     = optparse.OptionGroup(parser, 'Filesystem Options')
205     verbosity      = optparse.OptionGroup(parser, 'Verbosity / Simulation Options')
206
207     general.add_option('-h', '--help',
208             action='help', help='print this help text and exit')
209     general.add_option('-v', '--version',
210             action='version', help='print program version and exit')
211     general.add_option('-U', '--update',
212             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)')
213     general.add_option('-i', '--ignore-errors',
214             action='store_true', dest='ignoreerrors', help='continue on download errors, for example to skip unavailable videos in a playlist', default=False)
215     general.add_option('--abort-on-error',
216             action='store_false', dest='ignoreerrors',
217             help='Abort downloading of further videos (in the playlist or the command line) if an error occurs')
218     general.add_option('--dump-user-agent',
219             action='store_true', dest='dump_user_agent',
220             help='display the current browser identification', default=False)
221     general.add_option('--user-agent',
222             dest='user_agent', help='specify a custom user agent', metavar='UA')
223     general.add_option('--referer',
224             dest='referer', help='specify a custom referer, use if the video access is restricted to one domain',
225             metavar='REF', default=None)
226     general.add_option('--list-extractors',
227             action='store_true', dest='list_extractors',
228             help='List all supported extractors and the URLs they would handle', default=False)
229     general.add_option('--extractor-descriptions',
230             action='store_true', dest='list_extractor_descriptions',
231             help='Output descriptions of all supported extractors', default=False)
232     general.add_option(
233         '--proxy', dest='proxy', default=None, metavar='URL',
234         help='Use the specified HTTP/HTTPS proxy. Pass in an empty string (--proxy "") for direct connection')
235     general.add_option('--no-check-certificate', action='store_true', dest='no_check_certificate', default=False, help='Suppress HTTPS certificate validation.')
236     general.add_option(
237         '--cache-dir', dest='cachedir', default=get_cachedir(), metavar='DIR',
238         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.')
239     general.add_option(
240         '--no-cache-dir', action='store_const', const=None, dest='cachedir',
241         help='Disable filesystem caching')
242     general.add_option(
243         '--socket-timeout', dest='socket_timeout',
244         type=float, default=None, help=u'Time to wait before giving up, in seconds')
245     general.add_option(
246         '--bidi-workaround', dest='bidi_workaround', action='store_true',
247         help=u'Work around terminals that lack bidirectional text support. Requires bidiv or fribidi executable in PATH')
248     general.add_option('--default-search',
249             dest='default_search', metavar='PREFIX',
250             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.')
251     general.add_option(
252         '--ignore-config',
253         action='store_true',
254         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)')
255
256
257     selection.add_option(
258         '--playlist-start',
259         dest='playliststart', metavar='NUMBER', default=1, type=int,
260         help='playlist video to start at (default is %default)')
261     selection.add_option(
262         '--playlist-end',
263         dest='playlistend', metavar='NUMBER', default=None, type=int,
264         help='playlist video to end at (default is last)')
265     selection.add_option('--match-title', dest='matchtitle', metavar='REGEX',help='download only matching titles (regex or caseless sub-string)')
266     selection.add_option('--reject-title', dest='rejecttitle', metavar='REGEX',help='skip download for matching titles (regex or caseless sub-string)')
267     selection.add_option('--max-downloads', metavar='NUMBER',
268                          dest='max_downloads', type=int, default=None,
269                          help='Abort after downloading NUMBER files')
270     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)
271     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)
272     selection.add_option('--date', metavar='DATE', dest='date', help='download only videos uploaded in this date', default=None)
273     selection.add_option(
274         '--datebefore', metavar='DATE', dest='datebefore', default=None,
275         help='download only videos uploaded on or before this date (i.e. inclusive)')
276     selection.add_option(
277         '--dateafter', metavar='DATE', dest='dateafter', default=None,
278         help='download only videos uploaded on or after this date (i.e. inclusive)')
279     selection.add_option(
280         '--min-views', metavar='COUNT', dest='min_views',
281         default=None, type=int,
282         help="Do not download any videos with less than COUNT views",)
283     selection.add_option(
284         '--max-views', metavar='COUNT', dest='max_views',
285         default=None, type=int,
286         help="Do not download any videos with more than COUNT views",)
287     selection.add_option('--no-playlist', action='store_true', dest='noplaylist', help='download only the currently playing video', default=False)
288     selection.add_option('--age-limit', metavar='YEARS', dest='age_limit',
289                          help='download only videos suitable for the given age',
290                          default=None, type=int)
291     selection.add_option('--download-archive', metavar='FILE',
292                          dest='download_archive',
293                          help='Download only videos not listed in the archive file. Record the IDs of all downloaded videos in it.')
294     selection.add_option(
295         '--include-ads', dest='include_ads',
296         action='store_true',
297         help='Download advertisements as well (experimental)')
298     selection.add_option(
299         '--youtube-include-dash-manifest', action='store_true',
300         dest='youtube_include_dash_manifest', default=False,
301         help='Try to download the DASH manifest on YouTube videos (experimental)')
302
303     authentication.add_option('-u', '--username',
304             dest='username', metavar='USERNAME', help='account username')
305     authentication.add_option('-p', '--password',
306             dest='password', metavar='PASSWORD', help='account password')
307     authentication.add_option('-n', '--netrc',
308             action='store_true', dest='usenetrc', help='use .netrc authentication data', default=False)
309     authentication.add_option('--video-password',
310             dest='videopassword', metavar='PASSWORD', help='video password (vimeo, smotri)')
311
312
313     video_format.add_option('-f', '--format',
314             action='store', dest='format', metavar='FORMAT', default=None,
315             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.')
316     video_format.add_option('--all-formats',
317             action='store_const', dest='format', help='download all available video formats', const='all')
318     video_format.add_option('--prefer-free-formats',
319             action='store_true', dest='prefer_free_formats', default=False, help='prefer free video formats unless a specific one is requested')
320     video_format.add_option('--max-quality',
321             action='store', dest='format_limit', metavar='FORMAT', help='highest quality format to download')
322     video_format.add_option('-F', '--list-formats',
323             action='store_true', dest='listformats', help='list all available formats')
324
325     subtitles.add_option('--write-sub', '--write-srt',
326             action='store_true', dest='writesubtitles',
327             help='write subtitle file', default=False)
328     subtitles.add_option('--write-auto-sub', '--write-automatic-sub',
329             action='store_true', dest='writeautomaticsub',
330             help='write automatic subtitle file (youtube only)', default=False)
331     subtitles.add_option('--all-subs',
332             action='store_true', dest='allsubtitles',
333             help='downloads all the available subtitles of the video', default=False)
334     subtitles.add_option('--list-subs',
335             action='store_true', dest='listsubtitles',
336             help='lists all available subtitles for the video', default=False)
337     subtitles.add_option('--sub-format',
338             action='store', dest='subtitlesformat', metavar='FORMAT',
339             help='subtitle format (default=srt) ([sbv/vtt] youtube only)', default='srt')
340     subtitles.add_option('--sub-lang', '--sub-langs', '--srt-lang',
341             action='callback', dest='subtitleslangs', metavar='LANGS', type='str',
342             default=[], callback=_comma_separated_values_options_callback,
343             help='languages of the subtitles to download (optional) separated by commas, use IETF language tags like \'en,pt\'')
344
345     downloader.add_option('-r', '--rate-limit',
346             dest='ratelimit', metavar='LIMIT', help='maximum download rate in bytes per second (e.g. 50K or 4.2M)')
347     downloader.add_option('-R', '--retries',
348             dest='retries', metavar='RETRIES', help='number of retries (default is %default)', default=10)
349     downloader.add_option('--buffer-size',
350             dest='buffersize', metavar='SIZE', help='size of download buffer (e.g. 1024 or 16K) (default is %default)', default="1024")
351     downloader.add_option('--no-resize-buffer',
352             action='store_true', dest='noresizebuffer',
353             help='do not automatically adjust the buffer size. By default, the buffer size is automatically resized from an initial value of SIZE.', default=False)
354     downloader.add_option('--test', action='store_true', dest='test', default=False, help=optparse.SUPPRESS_HELP)
355
356     verbosity.add_option('-q', '--quiet',
357             action='store_true', dest='quiet', help='activates quiet mode', default=False)
358     verbosity.add_option('-s', '--simulate',
359             action='store_true', dest='simulate', help='do not download the video and do not write anything to disk', default=False)
360     verbosity.add_option('--skip-download',
361             action='store_true', dest='skip_download', help='do not download the video', default=False)
362     verbosity.add_option('-g', '--get-url',
363             action='store_true', dest='geturl', help='simulate, quiet but print URL', default=False)
364     verbosity.add_option('-e', '--get-title',
365             action='store_true', dest='gettitle', help='simulate, quiet but print title', default=False)
366     verbosity.add_option('--get-id',
367             action='store_true', dest='getid', help='simulate, quiet but print id', default=False)
368     verbosity.add_option('--get-thumbnail',
369             action='store_true', dest='getthumbnail',
370             help='simulate, quiet but print thumbnail URL', default=False)
371     verbosity.add_option('--get-description',
372             action='store_true', dest='getdescription',
373             help='simulate, quiet but print video description', default=False)
374     verbosity.add_option('--get-duration',
375             action='store_true', dest='getduration',
376             help='simulate, quiet but print video length', default=False)
377     verbosity.add_option('--get-filename',
378             action='store_true', dest='getfilename',
379             help='simulate, quiet but print output filename', default=False)
380     verbosity.add_option('--get-format',
381             action='store_true', dest='getformat',
382             help='simulate, quiet but print output format', default=False)
383     verbosity.add_option('-j', '--dump-json',
384             action='store_true', dest='dumpjson',
385             help='simulate, quiet but print JSON information', default=False)
386     verbosity.add_option('--newline',
387             action='store_true', dest='progress_with_newline', help='output progress bar as new lines', default=False)
388     verbosity.add_option('--no-progress',
389             action='store_true', dest='noprogress', help='do not print progress bar', default=False)
390     verbosity.add_option('--console-title',
391             action='store_true', dest='consoletitle',
392             help='display progress in console titlebar', default=False)
393     verbosity.add_option('-v', '--verbose',
394             action='store_true', dest='verbose', help='print various debugging information', default=False)
395     verbosity.add_option('--dump-intermediate-pages',
396             action='store_true', dest='dump_intermediate_pages', default=False,
397             help='print downloaded pages to debug problems (very verbose)')
398     verbosity.add_option('--write-pages',
399             action='store_true', dest='write_pages', default=False,
400             help='Write downloaded intermediary pages to files in the current directory to debug problems')
401     verbosity.add_option('--youtube-print-sig-code',
402             action='store_true', dest='youtube_print_sig_code', default=False,
403             help=optparse.SUPPRESS_HELP)
404     verbosity.add_option('--print-traffic',
405             dest='debug_printtraffic', action='store_true', default=False,
406             help='Display sent and read HTTP traffic')
407
408
409     filesystem.add_option('-t', '--title',
410             action='store_true', dest='usetitle', help='use title in file name (default)', default=False)
411     filesystem.add_option('--id',
412             action='store_true', dest='useid', help='use only video ID in file name', default=False)
413     filesystem.add_option('-l', '--literal',
414             action='store_true', dest='usetitle', help='[deprecated] alias of --title', default=False)
415     filesystem.add_option('-A', '--auto-number',
416             action='store_true', dest='autonumber',
417             help='number downloaded files starting from 00000', default=False)
418     filesystem.add_option('-o', '--output',
419             dest='outtmpl', metavar='TEMPLATE',
420             help=('output filename template. Use %(title)s to get the title, '
421                   '%(uploader)s for the uploader name, %(uploader_id)s for the uploader nickname if different, '
422                   '%(autonumber)s to get an automatically incremented number, '
423                   '%(ext)s for the filename extension, '
424                   '%(format)s for the format description (like "22 - 1280x720" or "HD"), '
425                   '%(format_id)s for the unique id of the format (like Youtube\'s itags: "137"), '
426                   '%(upload_date)s for the upload date (YYYYMMDD), '
427                   '%(extractor)s for the provider (youtube, metacafe, etc), '
428                   '%(id)s for the video id, %(playlist)s for the playlist the video is in, '
429                   '%(playlist_index)s for the position in the playlist and %% for a literal percent. '
430                   'Use - to output to stdout. Can also be used to download to a different directory, '
431                   'for example with -o \'/my/downloads/%(uploader)s/%(title)s-%(id)s.%(ext)s\' .'))
432     filesystem.add_option('--autonumber-size',
433             dest='autonumber_size', metavar='NUMBER',
434             help='Specifies the number of digits in %(autonumber)s when it is present in output filename template or --auto-number option is given')
435     filesystem.add_option('--restrict-filenames',
436             action='store_true', dest='restrictfilenames',
437             help='Restrict filenames to only ASCII characters, and avoid "&" and spaces in filenames', default=False)
438     filesystem.add_option('-a', '--batch-file',
439             dest='batchfile', metavar='FILE', help='file containing URLs to download (\'-\' for stdin)')
440     filesystem.add_option('--load-info',
441             dest='load_info_filename', metavar='FILE',
442             help='json file containing the video information (created with the "--write-json" option)')
443     filesystem.add_option('-w', '--no-overwrites',
444             action='store_true', dest='nooverwrites', help='do not overwrite files', default=False)
445     filesystem.add_option('-c', '--continue',
446             action='store_true', dest='continue_dl', help='force resume of partially downloaded files. By default, youtube-dl will resume downloads if possible.', default=True)
447     filesystem.add_option('--no-continue',
448             action='store_false', dest='continue_dl',
449             help='do not resume partially downloaded files (restart from beginning)')
450     filesystem.add_option('--cookies',
451             dest='cookiefile', metavar='FILE', help='file to read cookies from and dump cookie jar in')
452     filesystem.add_option('--no-part',
453             action='store_true', dest='nopart', help='do not use .part files', default=False)
454     filesystem.add_option('--no-mtime',
455             action='store_false', dest='updatetime',
456             help='do not use the Last-modified header to set the file modification time', default=True)
457     filesystem.add_option('--write-description',
458             action='store_true', dest='writedescription',
459             help='write video description to a .description file', default=False)
460     filesystem.add_option('--write-info-json',
461             action='store_true', dest='writeinfojson',
462             help='write video metadata to a .info.json file', default=False)
463     filesystem.add_option('--write-annotations',
464             action='store_true', dest='writeannotations',
465             help='write video annotations to a .annotation file', default=False)
466     filesystem.add_option('--write-thumbnail',
467             action='store_true', dest='writethumbnail',
468             help='write thumbnail image to disk', default=False)
469
470
471     postproc.add_option('-x', '--extract-audio', action='store_true', dest='extractaudio', default=False,
472             help='convert video files to audio-only files (requires ffmpeg or avconv and ffprobe or avprobe)')
473     postproc.add_option('--audio-format', metavar='FORMAT', dest='audioformat', default='best',
474             help='"best", "aac", "vorbis", "mp3", "m4a", "opus", or "wav"; best by default')
475     postproc.add_option('--audio-quality', metavar='QUALITY', dest='audioquality', default='5',
476             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)')
477     postproc.add_option('--recode-video', metavar='FORMAT', dest='recodevideo', default=None,
478             help='Encode the video to another format if necessary (currently supported: mp4|flv|ogg|webm)')
479     postproc.add_option('-k', '--keep-video', action='store_true', dest='keepvideo', default=False,
480             help='keeps the video file on disk after the post-processing; the video is erased by default')
481     postproc.add_option('--no-post-overwrites', action='store_true', dest='nopostoverwrites', default=False,
482             help='do not overwrite post-processed files; the post-processed files are overwritten by default')
483     postproc.add_option('--embed-subs', action='store_true', dest='embedsubtitles', default=False,
484             help='embed subtitles in the video (only for mp4 videos)')
485     postproc.add_option('--add-metadata', action='store_true', dest='addmetadata', default=False,
486             help='write metadata to the video file')
487     postproc.add_option('--xattrs', action='store_true', dest='xattrs', default=False,
488             help='write metadata to the video file\'s xattrs (using dublin core and xdg standards)')
489     postproc.add_option('--prefer-avconv', action='store_false', dest='prefer_ffmpeg',
490         help='Prefer avconv over ffmpeg for running the postprocessors (default)')
491     postproc.add_option('--prefer-ffmpeg', action='store_true', dest='prefer_ffmpeg',
492         help='Prefer ffmpeg over avconv for running the postprocessors')
493
494
495     parser.add_option_group(general)
496     parser.add_option_group(selection)
497     parser.add_option_group(downloader)
498     parser.add_option_group(filesystem)
499     parser.add_option_group(verbosity)
500     parser.add_option_group(video_format)
501     parser.add_option_group(subtitles)
502     parser.add_option_group(authentication)
503     parser.add_option_group(postproc)
504
505     if overrideArguments is not None:
506         opts, args = parser.parse_args(overrideArguments)
507         if opts.verbose:
508             write_string(u'[debug] Override config: ' + repr(overrideArguments) + '\n')
509     else:
510         commandLineConf = sys.argv[1:]
511         if '--ignore-config' in commandLineConf:
512             systemConf = []
513             userConf = []
514         else:
515             systemConf = _readOptions('/etc/youtube-dl.conf')
516             if '--ignore-config' in systemConf:
517                 userConf = []
518             else:
519                 userConf = _readUserConf()
520         argv = systemConf + userConf + commandLineConf
521
522         opts, args = parser.parse_args(argv)
523         if opts.verbose:
524             write_string(u'[debug] System config: ' + repr(_hide_login_info(systemConf)) + '\n')
525             write_string(u'[debug] User config: ' + repr(_hide_login_info(userConf)) + '\n')
526             write_string(u'[debug] Command-line args: ' + repr(_hide_login_info(commandLineConf)) + '\n')
527             write_string(u'[debug] Encodings: locale %r, fs %r, out %r, pref: %r\n' %
528                          (locale.getpreferredencoding(), sys.getfilesystemencoding(), sys.stdout.encoding, preferredencoding()))
529
530     return parser, opts, args
531
532
533 def _real_main(argv=None):
534     # Compatibility fixes for Windows
535     if sys.platform == 'win32':
536         # https://github.com/rg3/youtube-dl/issues/820
537         codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else None)
538
539     setproctitle(u'youtube-dl')
540
541     parser, opts, args = parseOpts(argv)
542
543     # Set user agent
544     if opts.user_agent is not None:
545         std_headers['User-Agent'] = opts.user_agent
546
547     # Set referer
548     if opts.referer is not None:
549         std_headers['Referer'] = opts.referer
550
551     # Dump user agent
552     if opts.dump_user_agent:
553         compat_print(std_headers['User-Agent'])
554         sys.exit(0)
555
556     # Batch file verification
557     batch_urls = []
558     if opts.batchfile is not None:
559         try:
560             if opts.batchfile == '-':
561                 batchfd = sys.stdin
562             else:
563                 batchfd = io.open(opts.batchfile, 'r', encoding='utf-8', errors='ignore')
564             batch_urls = read_batch_urls(batchfd)
565             if opts.verbose:
566                 write_string(u'[debug] Batch file urls: ' + repr(batch_urls) + u'\n')
567         except IOError:
568             sys.exit(u'ERROR: batch file could not be read')
569     all_urls = batch_urls + 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')