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