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