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