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