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