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