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