[youtube] Make DASH manifest download conditional for now
[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     verbosity.add_option(
248         '--youtube-include-dash-manifest', action='store_true',
249         dest='youtube_include_dash_manifest', default=False,
250         help='Try to download the DASH manifest on YouTube videos (experimental)')
251
252     authentication.add_option('-u', '--username',
253             dest='username', metavar='USERNAME', help='account username')
254     authentication.add_option('-p', '--password',
255             dest='password', metavar='PASSWORD', help='account password')
256     authentication.add_option('-n', '--netrc',
257             action='store_true', dest='usenetrc', help='use .netrc authentication data', default=False)
258     authentication.add_option('--video-password',
259             dest='videopassword', metavar='PASSWORD', help='video password (vimeo, smotri)')
260
261
262     video_format.add_option('-f', '--format',
263             action='store', dest='format', metavar='FORMAT', default=None,
264             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", "worst", and "worstaudio"')
265     video_format.add_option('--all-formats',
266             action='store_const', dest='format', help='download all available video formats', const='all')
267     video_format.add_option('--prefer-free-formats',
268             action='store_true', dest='prefer_free_formats', default=False, help='prefer free video formats unless a specific one is requested')
269     video_format.add_option('--max-quality',
270             action='store', dest='format_limit', metavar='FORMAT', help='highest quality format to download')
271     video_format.add_option('-F', '--list-formats',
272             action='store_true', dest='listformats', help='list all available formats')
273
274     subtitles.add_option('--write-sub', '--write-srt',
275             action='store_true', dest='writesubtitles',
276             help='write subtitle file', default=False)
277     subtitles.add_option('--write-auto-sub', '--write-automatic-sub',
278             action='store_true', dest='writeautomaticsub',
279             help='write automatic subtitle file (youtube only)', default=False)
280     subtitles.add_option('--all-subs',
281             action='store_true', dest='allsubtitles',
282             help='downloads all the available subtitles of the video', default=False)
283     subtitles.add_option('--list-subs',
284             action='store_true', dest='listsubtitles',
285             help='lists all available subtitles for the video', default=False)
286     subtitles.add_option('--sub-format',
287             action='store', dest='subtitlesformat', metavar='FORMAT',
288             help='subtitle format (default=srt) ([sbv/vtt] youtube only)', default='srt')
289     subtitles.add_option('--sub-lang', '--sub-langs', '--srt-lang',
290             action='callback', dest='subtitleslangs', metavar='LANGS', type='str',
291             default=[], callback=_comma_separated_values_options_callback,
292             help='languages of the subtitles to download (optional) separated by commas, use IETF language tags like \'en,pt\'')
293
294     downloader.add_option('-r', '--rate-limit',
295             dest='ratelimit', metavar='LIMIT', help='maximum download rate in bytes per second (e.g. 50K or 4.2M)')
296     downloader.add_option('-R', '--retries',
297             dest='retries', metavar='RETRIES', help='number of retries (default is %default)', default=10)
298     downloader.add_option('--buffer-size',
299             dest='buffersize', metavar='SIZE', help='size of download buffer (e.g. 1024 or 16K) (default is %default)', default="1024")
300     downloader.add_option('--no-resize-buffer',
301             action='store_true', dest='noresizebuffer',
302             help='do not automatically adjust the buffer size. By default, the buffer size is automatically resized from an initial value of SIZE.', default=False)
303     downloader.add_option('--test', action='store_true', dest='test', default=False, help=optparse.SUPPRESS_HELP)
304
305     verbosity.add_option('-q', '--quiet',
306             action='store_true', dest='quiet', help='activates quiet mode', default=False)
307     verbosity.add_option('-s', '--simulate',
308             action='store_true', dest='simulate', help='do not download the video and do not write anything to disk', default=False)
309     verbosity.add_option('--skip-download',
310             action='store_true', dest='skip_download', help='do not download the video', default=False)
311     verbosity.add_option('-g', '--get-url',
312             action='store_true', dest='geturl', help='simulate, quiet but print URL', default=False)
313     verbosity.add_option('-e', '--get-title',
314             action='store_true', dest='gettitle', help='simulate, quiet but print title', default=False)
315     verbosity.add_option('--get-id',
316             action='store_true', dest='getid', help='simulate, quiet but print id', default=False)
317     verbosity.add_option('--get-thumbnail',
318             action='store_true', dest='getthumbnail',
319             help='simulate, quiet but print thumbnail URL', default=False)
320     verbosity.add_option('--get-description',
321             action='store_true', dest='getdescription',
322             help='simulate, quiet but print video description', default=False)
323     verbosity.add_option('--get-duration',
324             action='store_true', dest='getduration',
325             help='simulate, quiet but print video length', default=False)
326     verbosity.add_option('--get-filename',
327             action='store_true', dest='getfilename',
328             help='simulate, quiet but print output filename', default=False)
329     verbosity.add_option('--get-format',
330             action='store_true', dest='getformat',
331             help='simulate, quiet but print output format', default=False)
332     verbosity.add_option('-j', '--dump-json',
333             action='store_true', dest='dumpjson',
334             help='simulate, quiet but print JSON information', default=False)
335     verbosity.add_option('--newline',
336             action='store_true', dest='progress_with_newline', help='output progress bar as new lines', default=False)
337     verbosity.add_option('--no-progress',
338             action='store_true', dest='noprogress', help='do not print progress bar', default=False)
339     verbosity.add_option('--console-title',
340             action='store_true', dest='consoletitle',
341             help='display progress in console titlebar', default=False)
342     verbosity.add_option('-v', '--verbose',
343             action='store_true', dest='verbose', help='print various debugging information', default=False)
344     verbosity.add_option('--dump-intermediate-pages',
345             action='store_true', dest='dump_intermediate_pages', default=False,
346             help='print downloaded pages to debug problems (very verbose)')
347     verbosity.add_option('--write-pages',
348             action='store_true', dest='write_pages', default=False,
349             help='Write downloaded intermediary pages to files in the current directory to debug problems')
350     verbosity.add_option('--youtube-print-sig-code',
351             action='store_true', dest='youtube_print_sig_code', default=False,
352             help=optparse.SUPPRESS_HELP)
353     verbosity.add_option('--print-traffic',
354             dest='debug_printtraffic', action='store_true', default=False,
355             help='Display sent and read HTTP traffic')
356
357
358     filesystem.add_option('-t', '--title',
359             action='store_true', dest='usetitle', help='use title in file name (default)', default=False)
360     filesystem.add_option('--id',
361             action='store_true', dest='useid', help='use only video ID in file name', default=False)
362     filesystem.add_option('-l', '--literal',
363             action='store_true', dest='usetitle', help='[deprecated] alias of --title', default=False)
364     filesystem.add_option('-A', '--auto-number',
365             action='store_true', dest='autonumber',
366             help='number downloaded files starting from 00000', default=False)
367     filesystem.add_option('-o', '--output',
368             dest='outtmpl', metavar='TEMPLATE',
369             help=('output filename template. Use %(title)s to get the title, '
370                   '%(uploader)s for the uploader name, %(uploader_id)s for the uploader nickname if different, '
371                   '%(autonumber)s to get an automatically incremented number, '
372                   '%(ext)s for the filename extension, '
373                   '%(format)s for the format description (like "22 - 1280x720" or "HD"), '
374                   '%(format_id)s for the unique id of the format (like Youtube\'s itags: "137"), '
375                   '%(upload_date)s for the upload date (YYYYMMDD), '
376                   '%(extractor)s for the provider (youtube, metacafe, etc), '
377                   '%(id)s for the video id, %(playlist)s for the playlist the video is in, '
378                   '%(playlist_index)s for the position in the playlist and %% for a literal percent. '
379                   'Use - to output to stdout. Can also be used to download to a different directory, '
380                   'for example with -o \'/my/downloads/%(uploader)s/%(title)s-%(id)s.%(ext)s\' .'))
381     filesystem.add_option('--autonumber-size',
382             dest='autonumber_size', metavar='NUMBER',
383             help='Specifies the number of digits in %(autonumber)s when it is present in output filename template or --auto-number option is given')
384     filesystem.add_option('--restrict-filenames',
385             action='store_true', dest='restrictfilenames',
386             help='Restrict filenames to only ASCII characters, and avoid "&" and spaces in filenames', default=False)
387     filesystem.add_option('-a', '--batch-file',
388             dest='batchfile', metavar='FILE', help='file containing URLs to download (\'-\' for stdin)')
389     filesystem.add_option('--load-info',
390             dest='load_info_filename', metavar='FILE',
391             help='json file containing the video information (created with the "--write-json" option)')
392     filesystem.add_option('-w', '--no-overwrites',
393             action='store_true', dest='nooverwrites', help='do not overwrite files', default=False)
394     filesystem.add_option('-c', '--continue',
395             action='store_true', dest='continue_dl', help='force resume of partially downloaded files. By default, youtube-dl will resume downloads if possible.', default=True)
396     filesystem.add_option('--no-continue',
397             action='store_false', dest='continue_dl',
398             help='do not resume partially downloaded files (restart from beginning)')
399     filesystem.add_option('--cookies',
400             dest='cookiefile', metavar='FILE', help='file to read cookies from and dump cookie jar in')
401     filesystem.add_option('--no-part',
402             action='store_true', dest='nopart', help='do not use .part files', default=False)
403     filesystem.add_option('--no-mtime',
404             action='store_false', dest='updatetime',
405             help='do not use the Last-modified header to set the file modification time', default=True)
406     filesystem.add_option('--write-description',
407             action='store_true', dest='writedescription',
408             help='write video description to a .description file', default=False)
409     filesystem.add_option('--write-info-json',
410             action='store_true', dest='writeinfojson',
411             help='write video metadata to a .info.json file', default=False)
412     filesystem.add_option('--write-annotations',
413             action='store_true', dest='writeannotations',
414             help='write video annotations to a .annotation file', default=False)
415     filesystem.add_option('--write-thumbnail',
416             action='store_true', dest='writethumbnail',
417             help='write thumbnail image to disk', default=False)
418
419
420     postproc.add_option('-x', '--extract-audio', action='store_true', dest='extractaudio', default=False,
421             help='convert video files to audio-only files (requires ffmpeg or avconv and ffprobe or avprobe)')
422     postproc.add_option('--audio-format', metavar='FORMAT', dest='audioformat', default='best',
423             help='"best", "aac", "vorbis", "mp3", "m4a", "opus", or "wav"; best by default')
424     postproc.add_option('--audio-quality', metavar='QUALITY', dest='audioquality', default='5',
425             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)')
426     postproc.add_option('--recode-video', metavar='FORMAT', dest='recodevideo', default=None,
427             help='Encode the video to another format if necessary (currently supported: mp4|flv|ogg|webm)')
428     postproc.add_option('-k', '--keep-video', action='store_true', dest='keepvideo', default=False,
429             help='keeps the video file on disk after the post-processing; the video is erased by default')
430     postproc.add_option('--no-post-overwrites', action='store_true', dest='nopostoverwrites', default=False,
431             help='do not overwrite post-processed files; the post-processed files are overwritten by default')
432     postproc.add_option('--embed-subs', action='store_true', dest='embedsubtitles', default=False,
433             help='embed subtitles in the video (only for mp4 videos)')
434     postproc.add_option('--add-metadata', action='store_true', dest='addmetadata', default=False,
435             help='write metadata to the video file')
436     postproc.add_option('--xattrs', action='store_true', dest='xattrs', default=False,
437             help='write metadata to the video file\'s xattrs (using dublin core and xdg standards)')
438     postproc.add_option('--prefer-avconv', action='store_false', dest='prefer_ffmpeg',
439         help='Prefer avconv over ffmpeg for running the postprocessors (default)')
440     postproc.add_option('--prefer-ffmpeg', action='store_true', dest='prefer_ffmpeg',
441         help='Prefer ffmpeg over avconv for running the postprocessors')
442
443
444     parser.add_option_group(general)
445     parser.add_option_group(selection)
446     parser.add_option_group(downloader)
447     parser.add_option_group(filesystem)
448     parser.add_option_group(verbosity)
449     parser.add_option_group(video_format)
450     parser.add_option_group(subtitles)
451     parser.add_option_group(authentication)
452     parser.add_option_group(postproc)
453
454     if overrideArguments is not None:
455         opts, args = parser.parse_args(overrideArguments)
456         if opts.verbose:
457             write_string(u'[debug] Override config: ' + repr(overrideArguments) + '\n')
458     else:
459         systemConf = _readOptions('/etc/youtube-dl.conf')
460
461         xdg_config_home = os.environ.get('XDG_CONFIG_HOME')
462         if xdg_config_home:
463             userConfFile = os.path.join(xdg_config_home, 'youtube-dl', 'config')
464             if not os.path.isfile(userConfFile):
465                 userConfFile = os.path.join(xdg_config_home, 'youtube-dl.conf')
466         else:
467             userConfFile = os.path.join(os.path.expanduser('~'), '.config', 'youtube-dl', 'config')
468             if not os.path.isfile(userConfFile):
469                 userConfFile = os.path.join(os.path.expanduser('~'), '.config', 'youtube-dl.conf')
470         userConf = _readOptions(userConfFile, None)
471
472         if userConf is None:
473             appdata_dir = os.environ.get('appdata')
474             if appdata_dir:
475                 userConf = _readOptions(
476                     os.path.join(appdata_dir, 'youtube-dl', 'config'),
477                     default=None)
478                 if userConf is None:
479                     userConf = _readOptions(
480                         os.path.join(appdata_dir, 'youtube-dl', 'config.txt'),
481                         default=None)
482
483         if userConf is None:
484             userConf = _readOptions(
485                 os.path.join(os.path.expanduser('~'), 'youtube-dl.conf'),
486                 default=None)
487         if userConf is None:
488             userConf = _readOptions(
489                 os.path.join(os.path.expanduser('~'), 'youtube-dl.conf.txt'),
490                 default=None)
491
492         if userConf is None:
493             userConf = []
494
495         commandLineConf = sys.argv[1:]
496         argv = systemConf + userConf + commandLineConf
497         opts, args = parser.parse_args(argv)
498         if opts.verbose:
499             write_string(u'[debug] System config: ' + repr(_hide_login_info(systemConf)) + '\n')
500             write_string(u'[debug] User config: ' + repr(_hide_login_info(userConf)) + '\n')
501             write_string(u'[debug] Command-line args: ' + repr(_hide_login_info(commandLineConf)) + '\n')
502             write_string(u'[debug] Encodings: locale %r, fs %r, out %r, pref: %r\n' %
503                          (locale.getpreferredencoding(), sys.getfilesystemencoding(), sys.stdout.encoding, preferredencoding()))
504
505     return parser, opts, args
506
507
508 def _real_main(argv=None):
509     # Compatibility fixes for Windows
510     if sys.platform == 'win32':
511         # https://github.com/rg3/youtube-dl/issues/820
512         codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else None)
513
514     setproctitle(u'youtube-dl')
515
516     parser, opts, args = parseOpts(argv)
517
518     # Set user agent
519     if opts.user_agent is not None:
520         std_headers['User-Agent'] = opts.user_agent
521
522     # Set referer
523     if opts.referer is not None:
524         std_headers['Referer'] = opts.referer
525
526     # Dump user agent
527     if opts.dump_user_agent:
528         compat_print(std_headers['User-Agent'])
529         sys.exit(0)
530
531     # Batch file verification
532     batchurls = []
533     if opts.batchfile is not None:
534         try:
535             if opts.batchfile == '-':
536                 batchfd = sys.stdin
537             else:
538                 batchfd = open(opts.batchfile, 'r')
539             batchurls = batchfd.readlines()
540             batchurls = [x.strip() for x in batchurls]
541             batchurls = [x for x in batchurls if len(x) > 0 and not re.search(r'^[#/;]', x)]
542             if opts.verbose:
543                 write_string(u'[debug] Batch file urls: ' + repr(batchurls) + u'\n')
544         except IOError:
545             sys.exit(u'ERROR: batch file could not be read')
546     all_urls = batchurls + args
547     all_urls = [url.strip() for url in all_urls]
548     _enc = preferredencoding()
549     all_urls = [url.decode(_enc, 'ignore') if isinstance(url, bytes) else url for url in all_urls]
550
551     extractors = gen_extractors()
552
553     if opts.list_extractors:
554         for ie in sorted(extractors, key=lambda ie: ie.IE_NAME.lower()):
555             compat_print(ie.IE_NAME + (' (CURRENTLY BROKEN)' if not ie._WORKING else ''))
556             matchedUrls = [url for url in all_urls if ie.suitable(url)]
557             for mu in matchedUrls:
558                 compat_print(u'  ' + mu)
559         sys.exit(0)
560     if opts.list_extractor_descriptions:
561         for ie in sorted(extractors, key=lambda ie: ie.IE_NAME.lower()):
562             if not ie._WORKING:
563                 continue
564             desc = getattr(ie, 'IE_DESC', ie.IE_NAME)
565             if desc is False:
566                 continue
567             if hasattr(ie, 'SEARCH_KEY'):
568                 _SEARCHES = (u'cute kittens', u'slithering pythons', u'falling cat', u'angry poodle', u'purple fish', u'running tortoise')
569                 _COUNTS = (u'', u'5', u'10', u'all')
570                 desc += u' (Example: "%s%s:%s" )' % (ie.SEARCH_KEY, random.choice(_COUNTS), random.choice(_SEARCHES))
571             compat_print(desc)
572         sys.exit(0)
573
574
575     # Conflicting, missing and erroneous options
576     if opts.usenetrc and (opts.username is not None or opts.password is not None):
577         parser.error(u'using .netrc conflicts with giving username/password')
578     if opts.password is not None and opts.username is None:
579         parser.error(u'account username missing\n')
580     if opts.outtmpl is not None and (opts.usetitle or opts.autonumber or opts.useid):
581         parser.error(u'using output template conflicts with using title, video ID or auto number')
582     if opts.usetitle and opts.useid:
583         parser.error(u'using title conflicts with using video ID')
584     if opts.username is not None and opts.password is None:
585         opts.password = getpass.getpass(u'Type account password and press return:')
586     if opts.ratelimit is not None:
587         numeric_limit = FileDownloader.parse_bytes(opts.ratelimit)
588         if numeric_limit is None:
589             parser.error(u'invalid rate limit specified')
590         opts.ratelimit = numeric_limit
591     if opts.min_filesize is not None:
592         numeric_limit = FileDownloader.parse_bytes(opts.min_filesize)
593         if numeric_limit is None:
594             parser.error(u'invalid min_filesize specified')
595         opts.min_filesize = numeric_limit
596     if opts.max_filesize is not None:
597         numeric_limit = FileDownloader.parse_bytes(opts.max_filesize)
598         if numeric_limit is None:
599             parser.error(u'invalid max_filesize specified')
600         opts.max_filesize = numeric_limit
601     if opts.retries is not None:
602         try:
603             opts.retries = int(opts.retries)
604         except (TypeError, ValueError):
605             parser.error(u'invalid retry count specified')
606     if opts.buffersize is not None:
607         numeric_buffersize = FileDownloader.parse_bytes(opts.buffersize)
608         if numeric_buffersize is None:
609             parser.error(u'invalid buffer size specified')
610         opts.buffersize = numeric_buffersize
611     if opts.playliststart <= 0:
612         raise ValueError(u'Playlist start must be positive')
613     if opts.playlistend not in (-1, None) and opts.playlistend < opts.playliststart:
614         raise ValueError(u'Playlist end must be greater than playlist start')
615     if opts.extractaudio:
616         if opts.audioformat not in ['best', 'aac', 'mp3', 'm4a', 'opus', 'vorbis', 'wav']:
617             parser.error(u'invalid audio format specified')
618     if opts.audioquality:
619         opts.audioquality = opts.audioquality.strip('k').strip('K')
620         if not opts.audioquality.isdigit():
621             parser.error(u'invalid audio quality specified')
622     if opts.recodevideo is not None:
623         if opts.recodevideo not in ['mp4', 'flv', 'webm', 'ogg']:
624             parser.error(u'invalid video recode format specified')
625     if opts.date is not None:
626         date = DateRange.day(opts.date)
627     else:
628         date = DateRange(opts.dateafter, opts.datebefore)
629     if opts.default_search not in ('auto', None) and ':' not in opts.default_search:
630         parser.error(u'--default-search invalid; did you forget a colon (:) at the end?')
631
632     # Do not download videos when there are audio-only formats
633     if opts.extractaudio and not opts.keepvideo and opts.format is None:
634         opts.format = 'bestaudio/best'
635
636     # --all-sub automatically sets --write-sub if --write-auto-sub is not given
637     # this was the old behaviour if only --all-sub was given.
638     if opts.allsubtitles and (opts.writeautomaticsub == False):
639         opts.writesubtitles = True
640
641     if sys.version_info < (3,):
642         # In Python 2, sys.argv is a bytestring (also note http://bugs.python.org/issue2128 for Windows systems)
643         if opts.outtmpl is not None:
644             opts.outtmpl = opts.outtmpl.decode(preferredencoding())
645     outtmpl =((opts.outtmpl is not None and opts.outtmpl)
646             or (opts.format == '-1' and opts.usetitle and u'%(title)s-%(id)s-%(format)s.%(ext)s')
647             or (opts.format == '-1' and u'%(id)s-%(format)s.%(ext)s')
648             or (opts.usetitle and opts.autonumber and u'%(autonumber)s-%(title)s-%(id)s.%(ext)s')
649             or (opts.usetitle and u'%(title)s-%(id)s.%(ext)s')
650             or (opts.useid and u'%(id)s.%(ext)s')
651             or (opts.autonumber and u'%(autonumber)s-%(id)s.%(ext)s')
652             or u'%(title)s-%(id)s.%(ext)s')
653     if not os.path.splitext(outtmpl)[1] and opts.extractaudio:
654         parser.error(u'Cannot download a video and extract audio into the same'
655                      u' file! Use "{0}.%(ext)s" instead of "{0}" as the output'
656                      u' template'.format(outtmpl))
657
658     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
659     download_archive_fn = os.path.expanduser(opts.download_archive) if opts.download_archive is not None else opts.download_archive
660
661     ydl_opts = {
662         'usenetrc': opts.usenetrc,
663         'username': opts.username,
664         'password': opts.password,
665         'videopassword': opts.videopassword,
666         'quiet': (opts.quiet or any_printing),
667         'forceurl': opts.geturl,
668         'forcetitle': opts.gettitle,
669         'forceid': opts.getid,
670         'forcethumbnail': opts.getthumbnail,
671         'forcedescription': opts.getdescription,
672         'forceduration': opts.getduration,
673         'forcefilename': opts.getfilename,
674         'forceformat': opts.getformat,
675         'forcejson': opts.dumpjson,
676         'simulate': opts.simulate,
677         'skip_download': (opts.skip_download or opts.simulate or any_printing),
678         'format': opts.format,
679         'format_limit': opts.format_limit,
680         'listformats': opts.listformats,
681         'outtmpl': outtmpl,
682         'autonumber_size': opts.autonumber_size,
683         'restrictfilenames': opts.restrictfilenames,
684         'ignoreerrors': opts.ignoreerrors,
685         'ratelimit': opts.ratelimit,
686         'nooverwrites': opts.nooverwrites,
687         'retries': opts.retries,
688         'buffersize': opts.buffersize,
689         'noresizebuffer': opts.noresizebuffer,
690         'continuedl': opts.continue_dl,
691         'noprogress': opts.noprogress,
692         'progress_with_newline': opts.progress_with_newline,
693         'playliststart': opts.playliststart,
694         'playlistend': opts.playlistend,
695         'noplaylist': opts.noplaylist,
696         'logtostderr': opts.outtmpl == '-',
697         'consoletitle': opts.consoletitle,
698         'nopart': opts.nopart,
699         'updatetime': opts.updatetime,
700         'writedescription': opts.writedescription,
701         'writeannotations': opts.writeannotations,
702         'writeinfojson': opts.writeinfojson,
703         'writethumbnail': opts.writethumbnail,
704         'writesubtitles': opts.writesubtitles,
705         'writeautomaticsub': opts.writeautomaticsub,
706         'allsubtitles': opts.allsubtitles,
707         'listsubtitles': opts.listsubtitles,
708         'subtitlesformat': opts.subtitlesformat,
709         'subtitleslangs': opts.subtitleslangs,
710         'matchtitle': decodeOption(opts.matchtitle),
711         'rejecttitle': decodeOption(opts.rejecttitle),
712         'max_downloads': opts.max_downloads,
713         'prefer_free_formats': opts.prefer_free_formats,
714         'verbose': opts.verbose,
715         'dump_intermediate_pages': opts.dump_intermediate_pages,
716         'write_pages': opts.write_pages,
717         'test': opts.test,
718         'keepvideo': opts.keepvideo,
719         'min_filesize': opts.min_filesize,
720         'max_filesize': opts.max_filesize,
721         'min_views': opts.min_views,
722         'max_views': opts.max_views,
723         'daterange': date,
724         'cachedir': opts.cachedir,
725         'youtube_print_sig_code': opts.youtube_print_sig_code,
726         'age_limit': opts.age_limit,
727         'download_archive': download_archive_fn,
728         'cookiefile': opts.cookiefile,
729         'nocheckcertificate': opts.no_check_certificate,
730         'proxy': opts.proxy,
731         'socket_timeout': opts.socket_timeout,
732         'bidi_workaround': opts.bidi_workaround,
733         'debug_printtraffic': opts.debug_printtraffic,
734         'prefer_ffmpeg': opts.prefer_ffmpeg,
735         'include_ads': opts.include_ads,
736         'default_search': opts.default_search,
737         'youtube_include_dash_manifest': opts.youtube_include_dash_manifest,
738     }
739
740     with YoutubeDL(ydl_opts) as ydl:
741         ydl.print_debug_header()
742         ydl.add_default_info_extractors()
743
744         # PostProcessors
745         # Add the metadata pp first, the other pps will copy it
746         if opts.addmetadata:
747             ydl.add_post_processor(FFmpegMetadataPP())
748         if opts.extractaudio:
749             ydl.add_post_processor(FFmpegExtractAudioPP(preferredcodec=opts.audioformat, preferredquality=opts.audioquality, nopostoverwrites=opts.nopostoverwrites))
750         if opts.recodevideo:
751             ydl.add_post_processor(FFmpegVideoConvertor(preferedformat=opts.recodevideo))
752         if opts.embedsubtitles:
753             ydl.add_post_processor(FFmpegEmbedSubtitlePP(subtitlesformat=opts.subtitlesformat))
754         if opts.xattrs:
755             ydl.add_post_processor(XAttrMetadataPP())
756
757         # Update version
758         if opts.update_self:
759             update_self(ydl.to_screen, opts.verbose)
760
761         # Maybe do nothing
762         if (len(all_urls) < 1) and (opts.load_info_filename is None):
763             if not opts.update_self:
764                 parser.error(u'you must provide at least one URL')
765             else:
766                 sys.exit()
767
768         try:
769             if opts.load_info_filename is not None:
770                 retcode = ydl.download_with_info_file(opts.load_info_filename)
771             else:
772                 retcode = ydl.download(all_urls)
773         except MaxDownloadsReached:
774             ydl.to_screen(u'--max-download limit reached, aborting.')
775             retcode = 101
776
777     sys.exit(retcode)
778
779
780 def main(argv=None):
781     try:
782         _real_main(argv)
783     except DownloadError:
784         sys.exit(1)
785     except SameFileError:
786         sys.exit(u'ERROR: fixed output name but more than one file to download')
787     except KeyboardInterrupt:
788         sys.exit(u'\nERROR: Interrupted by user')