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