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