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