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