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