]> jfr.im git - yt-dlp.git/blame - youtube_dl/__init__.py
release 2014.08.23
[yt-dlp.git] / youtube_dl / __init__.py
CommitLineData
235b3ba4
PH
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
3906e6ce 4__authors__ = (
59ae15a5
PH
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',
88f6c78b 20 'Dave Vasilevsky',
2069acc6 21 'Jaime Marquínez Ferrándiz',
fffec3b9 22 'Jeff Crouse',
6aabe820 23 'Osama Khalid',
e8600d69 24 'Michael Walter',
95464f14 25 'M. Yasoob Ullah Khalid',
0ae456f0 26 'Julien Fraichard',
be74864a 27 'Johny Mo Swag',
df725153 28 'Axel Noack',
ba7a1de0 29 'Albert Kim',
4a55479f 30 'Pierre Rudloff',
085bea45 31 'Huarong Huo',
ac4f319b 32 'Ismael Mejía',
2dad310e 33 'Steffan \'Ruirize\' James',
a623df4c 34 'Andras Elso',
b5bdc269 35 'Jelle van der Waa',
d3793638 36 'Marcin Cieślak',
0a120f74 37 'Anton Larionov',
38b2db6a 38 'Takuya Tsuchida',
87968574 39 'Sergey M.',
b83be81d 40 'Michael Orlitzky',
e63fc1be 41 'Chris Gahan',
a7732b67 42 'Saimadhav Heblikar',
2a893862 43 'Mike Col',
6d784e87 44 'Oleg Prutz',
0cea52cc 45 'pulpe',
845d14d3 46 'Andreas Schmitz',
cbffec0c 47 'Michael Kaiser',
96d16370 48 'Niklas Laxström',
f38da667 49 'David Triendl',
a339d7ba 50 'Anthony Weems',
dc3727b6 51 'David Wagner',
2fcec131 52 'Juan C. Olivares',
855e2750 53 'Mattias Harrysson',
2cc0082d 54 'phaer',
610e47c8 55 'Sainyam Kapoor',
bbe99d26 56 'Nicolas Évrard',
ccdd34ed 57 'Jason Normore',
25dfe0eb 58 'Hoje Lee',
c2ef2923 59 'Adam Thalhammer',
d30d2815 60 'Georg Jähnig',
9cc977f1 61 'Ralf Haring',
ba4133c9 62 'Koki Takahashi',
3d55f280 63 'Ariset Llerena',
cdc22cb8 64 'Adam Malcontenti-Wilson',
1df0ae21 65 'Tobias Bell',
0d90e0f0 66 'Naglis Jonaitis',
3b09757b 67 'Charles Chen',
5e95cb27 68 'Hassaan Ali',
e48a2c64 69 'Dobrosław Żybort',
4665664c 70 'David Fabijan',
56ca04f6 71 'Sebastian Haas',
3f338cd6 72 'Alexander Kirk',
deda8ac3 73 'Erik Johnson',
ba7a1de0 74)
235b3ba4
PH
75
76__license__ = 'Public Domain'
235b3ba4 77
0d94f247 78import codecs
8f563f32 79import io
c9ed14e6 80import optparse
235b3ba4 81import os
0f818663 82import random
c9ed14e6 83import shlex
052421ff 84import shutil
235b3ba4 85import sys
235b3ba4 86
c496ca96 87
a4fd0415 88from .utils import (
e68301af 89 compat_getpass,
a4fd0415 90 compat_print,
a4fd0415 91 DateRange,
acd69589 92 DEFAULT_OUTTMPL,
a4fd0415 93 decodeOption,
1c088fa8 94 get_term_width,
a4fd0415
PH
95 DownloadError,
96 get_cachedir,
a4fd0415 97 MaxDownloadsReached,
a4fd0415 98 preferredencoding,
62e609ab 99 read_batch_urls,
a4fd0415 100 SameFileError,
e3946f98 101 setproctitle,
a4fd0415
PH
102 std_headers,
103 write_string,
a4fd0415 104)
d5ed35b6 105from .update import update_self
92a86f4c 106from .downloader import (
a4fd0415
PH
107 FileDownloader,
108)
0824c28c 109from .extractor import gen_extractors
dca08720 110from .version import __version__
8222d8de 111from .YoutubeDL import YoutubeDL
56327689 112from .postprocessor import (
0c14e2fb 113 AtomicParsleyPP,
149254d0 114 FFmpegAudioFixPP,
a4fd0415
PH
115 FFmpegMetadataPP,
116 FFmpegVideoConvertor,
117 FFmpegExtractAudioPP,
118 FFmpegEmbedSubtitlePP,
e63fc1be 119 XAttrMetadataPP,
a4fd0415
PH
120)
121
235b3ba4 122
75b5c590 123def parseOpts(overrideArguments=None):
fb27c229 124 def _readOptions(filename_bytes, default=[]):
59ae15a5
PH
125 try:
126 optionf = open(filename_bytes)
127 except IOError:
fb27c229 128 return default # silently skip if file is not present
59ae15a5 129 try:
a0eaa341
PH
130 res = []
131 for l in optionf:
132 res += shlex.split(l, comments=True)
59ae15a5
PH
133 finally:
134 optionf.close()
135 return res
136
588128d0
PH
137 def _readUserConf():
138 xdg_config_home = os.environ.get('XDG_CONFIG_HOME')
139 if xdg_config_home:
140 userConfFile = os.path.join(xdg_config_home, 'youtube-dl', 'config')
141 if not os.path.isfile(userConfFile):
142 userConfFile = os.path.join(xdg_config_home, 'youtube-dl.conf')
143 else:
144 userConfFile = os.path.join(os.path.expanduser('~'), '.config', 'youtube-dl', 'config')
145 if not os.path.isfile(userConfFile):
146 userConfFile = os.path.join(os.path.expanduser('~'), '.config', 'youtube-dl.conf')
147 userConf = _readOptions(userConfFile, None)
148
149 if userConf is None:
150 appdata_dir = os.environ.get('appdata')
151 if appdata_dir:
152 userConf = _readOptions(
153 os.path.join(appdata_dir, 'youtube-dl', 'config'),
154 default=None)
155 if userConf is None:
156 userConf = _readOptions(
157 os.path.join(appdata_dir, 'youtube-dl', 'config.txt'),
158 default=None)
159
160 if userConf is None:
161 userConf = _readOptions(
162 os.path.join(os.path.expanduser('~'), 'youtube-dl.conf'),
163 default=None)
164 if userConf is None:
165 userConf = _readOptions(
166 os.path.join(os.path.expanduser('~'), 'youtube-dl.conf.txt'),
167 default=None)
168
169 if userConf is None:
170 userConf = []
171
172 return userConf
173
59ae15a5
PH
174 def _format_option_string(option):
175 ''' ('-o', '--option') -> -o, --format METAVAR'''
176
177 opts = []
178
179 if option._short_opts:
180 opts.append(option._short_opts[0])
181 if option._long_opts:
182 opts.append(option._long_opts[0])
183 if len(opts) > 1:
184 opts.insert(1, ', ')
185
186 if option.takes_value(): opts.append(' %s' % option.metavar)
187
188 return "".join(opts)
189
d6e203b3
IM
190 def _comma_separated_values_options_callback(option, opt_str, value, parser):
191 setattr(parser.values, option.dest, value.split(','))
192
920ef077
JMF
193 def _hide_login_info(opts):
194 opts = list(opts)
3126050c 195 for private_opt in ['-p', '--password', '-u', '--username', '--video-password']:
920ef077
JMF
196 try:
197 i = opts.index(private_opt)
198 opts[i+1] = '<PRIVATE>'
199 except ValueError:
200 pass
201 return opts
202
59ae15a5
PH
203 max_width = 80
204 max_help_position = 80
205
206 # No need to wrap help messages if we're on a wide console
1c088fa8 207 columns = get_term_width()
59ae15a5
PH
208 if columns: max_width = columns
209
210 fmt = optparse.IndentedHelpFormatter(width=max_width, max_help_position=max_help_position)
211 fmt.format_option_strings = _format_option_string
212
213 kw = {
214 'version' : __version__,
215 'formatter' : fmt,
216 'usage' : '%prog [options] url [url...]',
217 'conflict_handler' : 'resolve',
218 }
219
220 parser = optparse.OptionParser(**kw)
221
222 # option groups
223 general = optparse.OptionGroup(parser, 'General Options')
224 selection = optparse.OptionGroup(parser, 'Video Selection')
225 authentication = optparse.OptionGroup(parser, 'Authentication Options')
226 video_format = optparse.OptionGroup(parser, 'Video Format Options')
505c28aa 227 subtitles = optparse.OptionGroup(parser, 'Subtitle Options')
0beb3add 228 downloader = optparse.OptionGroup(parser, 'Download Options')
59ae15a5
PH
229 postproc = optparse.OptionGroup(parser, 'Post-processing Options')
230 filesystem = optparse.OptionGroup(parser, 'Filesystem Options')
2fe3d240 231 workarounds = optparse.OptionGroup(parser, 'Workarounds')
59ae15a5
PH
232 verbosity = optparse.OptionGroup(parser, 'Verbosity / Simulation Options')
233
234 general.add_option('-h', '--help',
235 action='help', help='print this help text and exit')
236 general.add_option('-v', '--version',
237 action='version', help='print program version and exit')
238 general.add_option('-U', '--update',
f631c331 239 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)')
59ae15a5 240 general.add_option('-i', '--ignore-errors',
98c4b8fa 241 action='store_true', dest='ignoreerrors', help='continue on download errors, for example to skip unavailable videos in a playlist', default=False)
41fd7c7e
PH
242 general.add_option('--abort-on-error',
243 action='store_false', dest='ignoreerrors',
244 help='Abort downloading of further videos (in the playlist or the command line) if an error occurs')
59ae15a5
PH
245 general.add_option('--dump-user-agent',
246 action='store_true', dest='dump_user_agent',
247 help='display the current browser identification', default=False)
59ae15a5
PH
248 general.add_option('--list-extractors',
249 action='store_true', dest='list_extractors',
250 help='List all supported extractors and the URLs they would handle', default=False)
62067cb9 251 general.add_option('--extractor-descriptions',
0f818663
PH
252 action='store_true', dest='list_extractor_descriptions',
253 help='Output descriptions of all supported extractors', default=False)
cf6758d2
PH
254 general.add_option(
255 '--proxy', dest='proxy', default=None, metavar='URL',
256 help='Use the specified HTTP/HTTPS proxy. Pass in an empty string (--proxy "") for direct connection')
6ad14cab
PH
257 general.add_option(
258 '--socket-timeout', dest='socket_timeout',
9656ee5d 259 type=float, default=None, help=u'Time to wait before giving up, in seconds')
9c1fc022
PH
260 general.add_option(
261 '--default-search',
262 dest='default_search', metavar='PREFIX',
1f7ccb90 263 help='Use this prefix for unqualified URLs. For example "gvsearch2:" downloads two videos from google videos for youtube-dl "large apple". Use the value "auto" to let youtube-dl guess ("auto_warning" to emit a warning when guessing). "error" just throws an error. The default value "fixup_error" repairs broken URLs, but emits an error if this is not possible instead of searching.')
588128d0
PH
264 general.add_option(
265 '--ignore-config',
266 action='store_true',
267 help='Do not read configuration files. When given in the global configuration file /etc/youtube-dl.conf: do not read the user configuration in ~/.config/youtube-dl.conf (%APPDATA%/youtube-dl/config.txt on Windows)')
268
a19fd00c
PH
269 selection.add_option(
270 '--playlist-start',
271 dest='playliststart', metavar='NUMBER', default=1, type=int,
272 help='playlist video to start at (default is %default)')
273 selection.add_option(
274 '--playlist-end',
275 dest='playlistend', metavar='NUMBER', default=None, type=int,
276 help='playlist video to end at (default is last)')
59ae15a5
PH
277 selection.add_option('--match-title', dest='matchtitle', metavar='REGEX',help='download only matching titles (regex or caseless sub-string)')
278 selection.add_option('--reject-title', dest='rejecttitle', metavar='REGEX',help='skip download for matching titles (regex or caseless sub-string)')
0c75c3fa
PH
279 selection.add_option('--max-downloads', metavar='NUMBER',
280 dest='max_downloads', type=int, default=None,
281 help='Abort after downloading NUMBER files')
dbf2ba3d
PH
282 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)
283 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)
bd558525 284 selection.add_option('--date', metavar='DATE', dest='date', help='download only videos uploaded in this date', default=None)
bac268e2
PH
285 selection.add_option(
286 '--datebefore', metavar='DATE', dest='datebefore', default=None,
287 help='download only videos uploaded on or before this date (i.e. inclusive)')
288 selection.add_option(
289 '--dateafter', metavar='DATE', dest='dateafter', default=None,
290 help='download only videos uploaded on or after this date (i.e. inclusive)')
5fe18bdb
PH
291 selection.add_option(
292 '--min-views', metavar='COUNT', dest='min_views',
293 default=None, type=int,
294 help="Do not download any videos with less than COUNT views",)
295 selection.add_option(
296 '--max-views', metavar='COUNT', dest='max_views',
297 default=None, type=int,
298 help="Do not download any videos with more than COUNT views",)
47192f92 299 selection.add_option('--no-playlist', action='store_true', dest='noplaylist', help='download only the currently playing video', default=False)
8dbe9899
PH
300 selection.add_option('--age-limit', metavar='YEARS', dest='age_limit',
301 help='download only videos suitable for the given age',
302 default=None, type=int)
c1c9a79c
PH
303 selection.add_option('--download-archive', metavar='FILE',
304 dest='download_archive',
36a826a5 305 help='Download only videos not listed in the archive file. Record the IDs of all downloaded videos in it.')
7b0817e8
PH
306 selection.add_option(
307 '--include-ads', dest='include_ads',
308 action='store_true',
309 help='Download advertisements as well (experimental)')
8b1be5cd 310 selection.add_option(
4919603f
PH
311 '--youtube-include-dash-manifest', action='store_true',
312 dest='youtube_include_dash_manifest', default=False,
313 help='Try to download the DASH manifest on YouTube videos (experimental)')
9e982f9e 314
59ae15a5
PH
315 authentication.add_option('-u', '--username',
316 dest='username', metavar='USERNAME', help='account username')
317 authentication.add_option('-p', '--password',
318 dest='password', metavar='PASSWORD', help='account password')
319 authentication.add_option('-n', '--netrc',
320 action='store_true', dest='usenetrc', help='use .netrc authentication data', default=False)
c6c19746 321 authentication.add_option('--video-password',
67d28bff 322 dest='videopassword', metavar='PASSWORD', help='video password (vimeo, smotri)')
59ae15a5
PH
323
324
325 video_format.add_option('-f', '--format',
de3ef3ed 326 action='store', dest='format', metavar='FORMAT', default=None,
bc6d5978 327 help='video format code, specify the order of preference using slashes: "-f 22/17/18". "-f mp4" and "-f flv" are also supported. You can also use the special names "best", "bestvideo", "bestaudio", "worst", "worstvideo" and "worstaudio". By default, youtube-dl will pick the best quality.')
59ae15a5
PH
328 video_format.add_option('--all-formats',
329 action='store_const', dest='format', help='download all available video formats', const='all')
330 video_format.add_option('--prefer-free-formats',
331 action='store_true', dest='prefer_free_formats', default=False, help='prefer free video formats unless a specific one is requested')
332 video_format.add_option('--max-quality',
333 action='store', dest='format_limit', metavar='FORMAT', help='highest quality format to download')
334 video_format.add_option('-F', '--list-formats',
4950f308 335 action='store_true', dest='listformats', help='list all available formats')
505c28aa
IM
336
337 subtitles.add_option('--write-sub', '--write-srt',
59ae15a5 338 action='store_true', dest='writesubtitles',
953e32b2 339 help='write subtitle file', default=False)
505c28aa 340 subtitles.add_option('--write-auto-sub', '--write-automatic-sub',
b004821f 341 action='store_true', dest='writeautomaticsub',
953e32b2 342 help='write automatic subtitle file (youtube only)', default=False)
505c28aa 343 subtitles.add_option('--all-subs',
ae608b80 344 action='store_true', dest='allsubtitles',
953e32b2 345 help='downloads all the available subtitles of the video', default=False)
505c28aa 346 subtitles.add_option('--list-subs',
2a4093ea 347 action='store_true', dest='listsubtitles',
953e32b2 348 help='lists all available subtitles for the video', default=False)
505c28aa 349 subtitles.add_option('--sub-format',
c3ab8f86 350 action='store', dest='subtitlesformat', metavar='FORMAT',
953e32b2 351 help='subtitle format (default=srt) ([sbv/vtt] youtube only)', default='srt')
d6e203b3
IM
352 subtitles.add_option('--sub-lang', '--sub-langs', '--srt-lang',
353 action='callback', dest='subtitleslangs', metavar='LANGS', type='str',
354 default=[], callback=_comma_separated_values_options_callback,
355 help='languages of the subtitles to download (optional) separated by commas, use IETF language tags like \'en,pt\'')
59ae15a5 356
0beb3add 357 downloader.add_option('-r', '--rate-limit',
cd054fc4 358 dest='ratelimit', metavar='LIMIT', help='maximum download rate in bytes per second (e.g. 50K or 4.2M)')
0beb3add
PH
359 downloader.add_option('-R', '--retries',
360 dest='retries', metavar='RETRIES', help='number of retries (default is %default)', default=10)
361 downloader.add_option('--buffer-size',
cd054fc4 362 dest='buffersize', metavar='SIZE', help='size of download buffer (e.g. 1024 or 16K) (default is %default)', default="1024")
0beb3add
PH
363 downloader.add_option('--no-resize-buffer',
364 action='store_true', dest='noresizebuffer',
365 help='do not automatically adjust the buffer size. By default, the buffer size is automatically resized from an initial value of SIZE.', default=False)
366 downloader.add_option('--test', action='store_true', dest='test', default=False, help=optparse.SUPPRESS_HELP)
367
2fe3d240
PH
368 workarounds.add_option(
369 '--encoding', dest='encoding', metavar='ENCODING',
370 help='Force the specified encoding (experimental)')
371 workarounds.add_option(
372 '--no-check-certificate', action='store_true',
373 dest='no_check_certificate', default=False,
374 help='Suppress HTTPS certificate validation.')
375 workarounds.add_option(
376 '--prefer-insecure', '--prefer-unsecure', action='store_true', dest='prefer_insecure',
377 help='Use an unencrypted connection to retrieve information about the video. (Currently supported only for YouTube)')
378 workarounds.add_option(
379 '--user-agent', metavar='UA',
380 dest='user_agent', help='specify a custom user agent')
381 workarounds.add_option(
382 '--referer', metavar='REF',
383 dest='referer', default=None,
384 help='specify a custom referer, use if the video access is restricted to one domain',
385 )
386 workarounds.add_option(
387 '--add-header', metavar='FIELD:VALUE',
388 dest='headers', action='append',
389 help='specify a custom HTTP header and its value, separated by a colon \':\'. You can use this option multiple times',
390 )
10b04ff7
PH
391 workarounds.add_option(
392 '--bidi-workaround', dest='bidi_workaround', action='store_true',
393 help=u'Work around terminals that lack bidirectional text support. Requires bidiv or fribidi executable in PATH')
2fe3d240 394
59ae15a5
PH
395 verbosity.add_option('-q', '--quiet',
396 action='store_true', dest='quiet', help='activates quiet mode', default=False)
ad8915b7
PH
397 verbosity.add_option(
398 '--no-warnings',
399 dest='no_warnings', action='store_true', default=False,
400 help='Ignore warnings')
59ae15a5
PH
401 verbosity.add_option('-s', '--simulate',
402 action='store_true', dest='simulate', help='do not download the video and do not write anything to disk', default=False)
403 verbosity.add_option('--skip-download',
404 action='store_true', dest='skip_download', help='do not download the video', default=False)
405 verbosity.add_option('-g', '--get-url',
406 action='store_true', dest='geturl', help='simulate, quiet but print URL', default=False)
407 verbosity.add_option('-e', '--get-title',
408 action='store_true', dest='gettitle', help='simulate, quiet but print title', default=False)
1a2adf3f 409 verbosity.add_option('--get-id',
410 action='store_true', dest='getid', help='simulate, quiet but print id', default=False)
59ae15a5
PH
411 verbosity.add_option('--get-thumbnail',
412 action='store_true', dest='getthumbnail',
413 help='simulate, quiet but print thumbnail URL', default=False)
414 verbosity.add_option('--get-description',
415 action='store_true', dest='getdescription',
416 help='simulate, quiet but print video description', default=False)
525ef922
PH
417 verbosity.add_option('--get-duration',
418 action='store_true', dest='getduration',
419 help='simulate, quiet but print video length', default=False)
59ae15a5
PH
420 verbosity.add_option('--get-filename',
421 action='store_true', dest='getfilename',
422 help='simulate, quiet but print output filename', default=False)
423 verbosity.add_option('--get-format',
424 action='store_true', dest='getformat',
425 help='simulate, quiet but print output format', default=False)
9d153818
MF
426 verbosity.add_option('-j', '--dump-json',
427 action='store_true', dest='dumpjson',
330edf2d 428 help='simulate, quiet but print JSON information. See --output for a description of available keys.', default=False)
7311fef8 429 verbosity.add_option('--newline',
5717d91a 430 action='store_true', dest='progress_with_newline', help='output progress bar as new lines', default=False)
59ae15a5
PH
431 verbosity.add_option('--no-progress',
432 action='store_true', dest='noprogress', help='do not print progress bar', default=False)
433 verbosity.add_option('--console-title',
434 action='store_true', dest='consoletitle',
435 help='display progress in console titlebar', default=False)
436 verbosity.add_option('-v', '--verbose',
437 action='store_true', dest='verbose', help='print various debugging information', default=False)
855703e5
PH
438 verbosity.add_option('--dump-intermediate-pages',
439 action='store_true', dest='dump_intermediate_pages', default=False,
67d28bff 440 help='print downloaded pages to debug problems (very verbose)')
d41e6efc
PH
441 verbosity.add_option('--write-pages',
442 action='store_true', dest='write_pages', default=False,
06dcbb71 443 help='Write downloaded intermediary pages to files in the current directory to debug problems')
edf3e38e
PH
444 verbosity.add_option('--youtube-print-sig-code',
445 action='store_true', dest='youtube_print_sig_code', default=False,
446 help=optparse.SUPPRESS_HELP)
a0ddb8a2
PH
447 verbosity.add_option('--print-traffic',
448 dest='debug_printtraffic', action='store_true', default=False,
4919603f
PH
449 help='Display sent and read HTTP traffic')
450
59ae15a5 451
a11165ec
PH
452 filesystem.add_option('-a', '--batch-file',
453 dest='batchfile', metavar='FILE', help='file containing URLs to download (\'-\' for stdin)')
59ae15a5 454 filesystem.add_option('--id',
08b2ac74 455 action='store_true', dest='useid', help='use only video ID in file name', default=False)
59ae15a5
PH
456 filesystem.add_option('-A', '--auto-number',
457 action='store_true', dest='autonumber',
458 help='number downloaded files starting from 00000', default=False)
459 filesystem.add_option('-o', '--output',
74e3452b
JMF
460 dest='outtmpl', metavar='TEMPLATE',
461 help=('output filename template. Use %(title)s to get the title, '
462 '%(uploader)s for the uploader name, %(uploader_id)s for the uploader nickname if different, '
463 '%(autonumber)s to get an automatically incremented number, '
fdefe96b 464 '%(ext)s for the filename extension, '
67d28bff 465 '%(format)s for the format description (like "22 - 1280x720" or "HD"), '
466 '%(format_id)s for the unique id of the format (like Youtube\'s itags: "137"), '
fdefe96b 467 '%(upload_date)s for the upload date (YYYYMMDD), '
74e3452b 468 '%(extractor)s for the provider (youtube, metacafe, etc), '
67d28bff 469 '%(id)s for the video id, %(playlist)s for the playlist the video is in, '
74e3452b 470 '%(playlist_index)s for the position in the playlist and %% for a literal percent. '
17b75c0d
PH
471 '%(height)s and %(width)s for the width and height of the video format. '
472 '%(resolution)s for a textual description of the resolution of the video format. '
74e3452b
JMF
473 'Use - to output to stdout. Can also be used to download to a different directory, '
474 'for example with -o \'/my/downloads/%(uploader)s/%(title)s-%(id)s.%(ext)s\' .'))
213c31ae
SK
475 filesystem.add_option('--autonumber-size',
476 dest='autonumber_size', metavar='NUMBER',
2a9e9b21 477 help='Specifies the number of digits in %(autonumber)s when it is present in output filename template or --auto-number option is given')
59ae15a5
PH
478 filesystem.add_option('--restrict-filenames',
479 action='store_true', dest='restrictfilenames',
480 help='Restrict filenames to only ASCII characters, and avoid "&" and spaces in filenames', default=False)
a11165ec 481 filesystem.add_option('-t', '--title',
4e99f488 482 action='store_true', dest='usetitle', help='[deprecated] use title in file name (default)', default=False)
a11165ec
PH
483 filesystem.add_option('-l', '--literal',
484 action='store_true', dest='usetitle', help='[deprecated] alias of --title', default=False)
59ae15a5
PH
485 filesystem.add_option('-w', '--no-overwrites',
486 action='store_true', dest='nooverwrites', help='do not overwrite files', default=False)
487 filesystem.add_option('-c', '--continue',
d4b7da84 488 action='store_true', dest='continue_dl', help='force resume of partially downloaded files. By default, youtube-dl will resume downloads if possible.', default=True)
59ae15a5
PH
489 filesystem.add_option('--no-continue',
490 action='store_false', dest='continue_dl',
491 help='do not resume partially downloaded files (restart from beginning)')
59ae15a5
PH
492 filesystem.add_option('--no-part',
493 action='store_true', dest='nopart', help='do not use .part files', default=False)
494 filesystem.add_option('--no-mtime',
495 action='store_false', dest='updatetime',
496 help='do not use the Last-modified header to set the file modification time', default=True)
497 filesystem.add_option('--write-description',
498 action='store_true', dest='writedescription',
499 help='write video description to a .description file', default=False)
500 filesystem.add_option('--write-info-json',
501 action='store_true', dest='writeinfojson',
502 help='write video metadata to a .info.json file', default=False)
1fb07d10
JG
503 filesystem.add_option('--write-annotations',
504 action='store_true', dest='writeannotations',
505 help='write video annotations to a .annotation file', default=False)
11d9224e
PH
506 filesystem.add_option('--write-thumbnail',
507 action='store_true', dest='writethumbnail',
508 help='write thumbnail image to disk', default=False)
a11165ec
PH
509 filesystem.add_option('--load-info',
510 dest='load_info_filename', metavar='FILE',
511 help='json file containing the video information (created with the "--write-json" option)')
512 filesystem.add_option('--cookies',
513 dest='cookiefile', metavar='FILE', help='file to read cookies from and dump cookie jar in')
fbb2fc55
PH
514 filesystem.add_option(
515 '--cache-dir', dest='cachedir', default=get_cachedir(), metavar='DIR',
516 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.')
517 filesystem.add_option(
518 '--no-cache-dir', action='store_const', const=None, dest='cachedir',
519 help='Disable filesystem caching')
052421ff
PH
520 filesystem.add_option(
521 '--rm-cache-dir', action='store_true', dest='rm_cachedir',
522 help='Delete all filesystem cache files')
59ae15a5
PH
523
524
525 postproc.add_option('-x', '--extract-audio', action='store_true', dest='extractaudio', default=False,
526 help='convert video files to audio-only files (requires ffmpeg or avconv and ffprobe or avprobe)')
527 postproc.add_option('--audio-format', metavar='FORMAT', dest='audioformat', default='best',
510e6f6d 528 help='"best", "aac", "vorbis", "mp3", "m4a", "opus", or "wav"; best by default')
59ae15a5
PH
529 postproc.add_option('--audio-quality', metavar='QUALITY', dest='audioquality', default='5',
530 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)')
7851b379 531 postproc.add_option('--recode-video', metavar='FORMAT', dest='recodevideo', default=None,
133af938 532 help='Encode the video to another format if necessary (currently supported: mp4|flv|ogg|webm|mkv)')
59ae15a5
PH
533 postproc.add_option('-k', '--keep-video', action='store_true', dest='keepvideo', default=False,
534 help='keeps the video file on disk after the post-processing; the video is erased by default')
f0648fc1
BPG
535 postproc.add_option('--no-post-overwrites', action='store_true', dest='nopostoverwrites', default=False,
536 help='do not overwrite post-processed files; the post-processed files are overwritten by default')
d4051a8e
JMF
537 postproc.add_option('--embed-subs', action='store_true', dest='embedsubtitles', default=False,
538 help='embed subtitles in the video (only for mp4 videos)')
0c14e2fb 539 postproc.add_option('--embed-thumbnail', action='store_true', dest='embedthumbnail', default=False,
540 help='embed thumbnail in the audio as cover art')
bc4f2917 541 postproc.add_option('--add-metadata', action='store_true', dest='addmetadata', default=False,
e63fc1be 542 help='write metadata to the video file')
543 postproc.add_option('--xattrs', action='store_true', dest='xattrs', default=False,
544 help='write metadata to the video file\'s xattrs (using dublin core and xdg standards)')
76b1bd67
JMF
545 postproc.add_option('--prefer-avconv', action='store_false', dest='prefer_ffmpeg',
546 help='Prefer avconv over ffmpeg for running the postprocessors (default)')
547 postproc.add_option('--prefer-ffmpeg', action='store_true', dest='prefer_ffmpeg',
548 help='Prefer ffmpeg over avconv for running the postprocessors')
59ae15a5
PH
549
550
551 parser.add_option_group(general)
552 parser.add_option_group(selection)
0beb3add 553 parser.add_option_group(downloader)
59ae15a5
PH
554 parser.add_option_group(filesystem)
555 parser.add_option_group(verbosity)
2fe3d240 556 parser.add_option_group(workarounds)
59ae15a5 557 parser.add_option_group(video_format)
505c28aa 558 parser.add_option_group(subtitles)
59ae15a5
PH
559 parser.add_option_group(authentication)
560 parser.add_option_group(postproc)
561
75b5c590
PH
562 if overrideArguments is not None:
563 opts, args = parser.parse_args(overrideArguments)
564 if opts.verbose:
7459e3a2 565 write_string(u'[debug] Override config: ' + repr(overrideArguments) + '\n')
59ae15a5 566 else:
1865ed31 567 commandLineConf = sys.argv[1:]
588128d0
PH
568 if '--ignore-config' in commandLineConf:
569 systemConf = []
570 userConf = []
571 else:
572 systemConf = _readOptions('/etc/youtube-dl.conf')
573 if '--ignore-config' in systemConf:
574 userConf = []
575 else:
576 userConf = _readUserConf()
75b5c590 577 argv = systemConf + userConf + commandLineConf
588128d0 578
75b5c590 579 opts, args = parser.parse_args(argv)
c76cb6d5 580 if opts.verbose:
7459e3a2
PH
581 write_string(u'[debug] System config: ' + repr(_hide_login_info(systemConf)) + '\n')
582 write_string(u'[debug] User config: ' + repr(_hide_login_info(userConf)) + '\n')
583 write_string(u'[debug] Command-line args: ' + repr(_hide_login_info(commandLineConf)) + '\n')
8c42c506 584
59ae15a5 585 return parser, opts, args
235b3ba4 586
e3946f98 587
b8ad4f02 588def _real_main(argv=None):
0d94f247
PH
589 # Compatibility fixes for Windows
590 if sys.platform == 'win32':
591 # https://github.com/rg3/youtube-dl/issues/820
592 codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else None)
593
e3946f98
PH
594 setproctitle(u'youtube-dl')
595
b8ad4f02 596 parser, opts, args = parseOpts(argv)
59ae15a5 597
59ae15a5
PH
598 # Set user agent
599 if opts.user_agent is not None:
600 std_headers['User-Agent'] = opts.user_agent
1865ed31 601
28535652
BH
602 # Set referer
603 if opts.referer is not None:
604 std_headers['Referer'] = opts.referer
59ae15a5 605
410afb20
AA
606 # Custom HTTP headers
607 if opts.headers is not None:
608 for h in opts.headers:
609 if h.find(':', 1) < 0:
610 parser.error(u'wrong header formatting, it should be key:value, not "%s"'%h)
611 key, value = h.split(':', 2)
612 if opts.verbose:
613 write_string(u'[debug] Adding header from command line option %s:%s\n'%(key, value))
614 std_headers[key] = value
615
59ae15a5
PH
616 # Dump user agent
617 if opts.dump_user_agent:
93eb15c5 618 compat_print(std_headers['User-Agent'])
59ae15a5
PH
619 sys.exit(0)
620
621 # Batch file verification
62e609ab 622 batch_urls = []
59ae15a5
PH
623 if opts.batchfile is not None:
624 try:
625 if opts.batchfile == '-':
626 batchfd = sys.stdin
627 else:
62e609ab
PH
628 batchfd = io.open(opts.batchfile, 'r', encoding='utf-8', errors='ignore')
629 batch_urls = read_batch_urls(batchfd)
05afc96b 630 if opts.verbose:
62e609ab 631 write_string(u'[debug] Batch file urls: ' + repr(batch_urls) + u'\n')
59ae15a5
PH
632 except IOError:
633 sys.exit(u'ERROR: batch file could not be read')
62e609ab 634 all_urls = batch_urls + args
59ae15a5 635 all_urls = [url.strip() for url in all_urls]
c774b3c6 636 _enc = preferredencoding()
41292a38 637 all_urls = [url.decode(_enc, 'ignore') if isinstance(url, bytes) else url for url in all_urls]
59ae15a5 638
59ae15a5
PH
639 extractors = gen_extractors()
640
641 if opts.list_extractors:
7dba9cd0 642 for ie in sorted(extractors, key=lambda ie: ie.IE_NAME.lower()):
93eb15c5 643 compat_print(ie.IE_NAME + (' (CURRENTLY BROKEN)' if not ie._WORKING else ''))
1a2c3c0f 644 matchedUrls = [url for url in all_urls if ie.suitable(url)]
59ae15a5 645 for mu in matchedUrls:
93eb15c5 646 compat_print(u' ' + mu)
59ae15a5 647 sys.exit(0)
0f818663
PH
648 if opts.list_extractor_descriptions:
649 for ie in sorted(extractors, key=lambda ie: ie.IE_NAME.lower()):
650 if not ie._WORKING:
651 continue
652 desc = getattr(ie, 'IE_DESC', ie.IE_NAME)
15870e90
PH
653 if desc is False:
654 continue
0f818663 655 if hasattr(ie, 'SEARCH_KEY'):
53eb2176 656 _SEARCHES = (u'cute kittens', u'slithering pythons', u'falling cat', u'angry poodle', u'purple fish', u'running tortoise', u'sleeping bunny')
0f818663
PH
657 _COUNTS = (u'', u'5', u'10', u'all')
658 desc += u' (Example: "%s%s:%s" )' % (ie.SEARCH_KEY, random.choice(_COUNTS), random.choice(_SEARCHES))
659 compat_print(desc)
660 sys.exit(0)
661
59ae15a5
PH
662
663 # Conflicting, missing and erroneous options
664 if opts.usenetrc and (opts.username is not None or opts.password is not None):
665 parser.error(u'using .netrc conflicts with giving username/password')
666 if opts.password is not None and opts.username is None:
67d28bff 667 parser.error(u'account username missing\n')
59ae15a5
PH
668 if opts.outtmpl is not None and (opts.usetitle or opts.autonumber or opts.useid):
669 parser.error(u'using output template conflicts with using title, video ID or auto number')
670 if opts.usetitle and opts.useid:
671 parser.error(u'using title conflicts with using video ID')
672 if opts.username is not None and opts.password is None:
e68301af 673 opts.password = compat_getpass(u'Type account password and press [Return]: ')
59ae15a5
PH
674 if opts.ratelimit is not None:
675 numeric_limit = FileDownloader.parse_bytes(opts.ratelimit)
676 if numeric_limit is None:
677 parser.error(u'invalid rate limit specified')
678 opts.ratelimit = numeric_limit
9e982f9e
JC
679 if opts.min_filesize is not None:
680 numeric_limit = FileDownloader.parse_bytes(opts.min_filesize)
681 if numeric_limit is None:
682 parser.error(u'invalid min_filesize specified')
683 opts.min_filesize = numeric_limit
684 if opts.max_filesize is not None:
685 numeric_limit = FileDownloader.parse_bytes(opts.max_filesize)
686 if numeric_limit is None:
687 parser.error(u'invalid max_filesize specified')
688 opts.max_filesize = numeric_limit
59ae15a5
PH
689 if opts.retries is not None:
690 try:
691 opts.retries = int(opts.retries)
dca08720 692 except (TypeError, ValueError):
59ae15a5
PH
693 parser.error(u'invalid retry count specified')
694 if opts.buffersize is not None:
695 numeric_buffersize = FileDownloader.parse_bytes(opts.buffersize)
696 if numeric_buffersize is None:
697 parser.error(u'invalid buffer size specified')
698 opts.buffersize = numeric_buffersize
a19fd00c
PH
699 if opts.playliststart <= 0:
700 raise ValueError(u'Playlist start must be positive')
701 if opts.playlistend not in (-1, None) and opts.playlistend < opts.playliststart:
702 raise ValueError(u'Playlist end must be greater than playlist start')
59ae15a5 703 if opts.extractaudio:
510e6f6d 704 if opts.audioformat not in ['best', 'aac', 'mp3', 'm4a', 'opus', 'vorbis', 'wav']:
59ae15a5
PH
705 parser.error(u'invalid audio format specified')
706 if opts.audioquality:
707 opts.audioquality = opts.audioquality.strip('k').strip('K')
708 if not opts.audioquality.isdigit():
709 parser.error(u'invalid audio quality specified')
7851b379 710 if opts.recodevideo is not None:
b7d73595 711 if opts.recodevideo not in ['mp4', 'flv', 'webm', 'ogg', 'mkv']:
7851b379 712 parser.error(u'invalid video recode format specified')
bd558525
JMF
713 if opts.date is not None:
714 date = DateRange.day(opts.date)
715 else:
716 date = DateRange(opts.dateafter, opts.datebefore)
1f7ccb90 717 if opts.default_search not in ('auto', 'auto_warning', 'error', 'fixup_error', None) and ':' not in opts.default_search:
04b4d394 718 parser.error(u'--default-search invalid; did you forget a colon (:) at the end?')
59ae15a5 719
de3ef3ed
PH
720 # Do not download videos when there are audio-only formats
721 if opts.extractaudio and not opts.keepvideo and opts.format is None:
722 opts.format = 'bestaudio/best'
723
0b7f3118
JMF
724 # --all-sub automatically sets --write-sub if --write-auto-sub is not given
725 # this was the old behaviour if only --all-sub was given.
726 if opts.allsubtitles and (opts.writeautomaticsub == False):
727 opts.writesubtitles = True
728
5cb9c312
PH
729 if sys.version_info < (3,):
730 # In Python 2, sys.argv is a bytestring (also note http://bugs.python.org/issue2128 for Windows systems)
0be41ec2
PH
731 if opts.outtmpl is not None:
732 opts.outtmpl = opts.outtmpl.decode(preferredencoding())
5cb9c312
PH
733 outtmpl =((opts.outtmpl is not None and opts.outtmpl)
734 or (opts.format == '-1' and opts.usetitle and u'%(title)s-%(id)s-%(format)s.%(ext)s')
735 or (opts.format == '-1' and u'%(id)s-%(format)s.%(ext)s')
736 or (opts.usetitle and opts.autonumber and u'%(autonumber)s-%(title)s-%(id)s.%(ext)s')
737 or (opts.usetitle and u'%(title)s-%(id)s.%(ext)s')
738 or (opts.useid and u'%(id)s.%(ext)s')
739 or (opts.autonumber and u'%(autonumber)s-%(id)s.%(ext)s')
acd69589 740 or DEFAULT_OUTTMPL)
dca02c80 741 if not os.path.splitext(outtmpl)[1] and opts.extractaudio:
b61067fa 742 parser.error(u'Cannot download a video and extract audio into the same'
dca02c80
JMF
743 u' file! Use "{0}.%(ext)s" instead of "{0}" as the output'
744 u' template'.format(outtmpl))
29c7a63d 745
525ef922 746 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
17093b83 747 download_archive_fn = os.path.expanduser(opts.download_archive) if opts.download_archive is not None else opts.download_archive
525ef922 748
bdde425c 749 ydl_opts = {
59ae15a5
PH
750 'usenetrc': opts.usenetrc,
751 'username': opts.username,
752 'password': opts.password,
c6c19746 753 'videopassword': opts.videopassword,
525ef922 754 'quiet': (opts.quiet or any_printing),
ad8915b7 755 'no_warnings': opts.no_warnings,
59ae15a5
PH
756 'forceurl': opts.geturl,
757 'forcetitle': opts.gettitle,
1a2adf3f 758 'forceid': opts.getid,
59ae15a5
PH
759 'forcethumbnail': opts.getthumbnail,
760 'forcedescription': opts.getdescription,
525ef922 761 'forceduration': opts.getduration,
59ae15a5
PH
762 'forcefilename': opts.getfilename,
763 'forceformat': opts.getformat,
9d153818 764 'forcejson': opts.dumpjson,
59ae15a5 765 'simulate': opts.simulate,
525ef922 766 'skip_download': (opts.skip_download or opts.simulate or any_printing),
59ae15a5
PH
767 'format': opts.format,
768 'format_limit': opts.format_limit,
769 'listformats': opts.listformats,
5cb9c312 770 'outtmpl': outtmpl,
213c31ae 771 'autonumber_size': opts.autonumber_size,
59ae15a5
PH
772 'restrictfilenames': opts.restrictfilenames,
773 'ignoreerrors': opts.ignoreerrors,
774 'ratelimit': opts.ratelimit,
775 'nooverwrites': opts.nooverwrites,
776 'retries': opts.retries,
777 'buffersize': opts.buffersize,
778 'noresizebuffer': opts.noresizebuffer,
779 'continuedl': opts.continue_dl,
780 'noprogress': opts.noprogress,
5717d91a 781 'progress_with_newline': opts.progress_with_newline,
59ae15a5
PH
782 'playliststart': opts.playliststart,
783 'playlistend': opts.playlistend,
47192f92 784 'noplaylist': opts.noplaylist,
59ae15a5
PH
785 'logtostderr': opts.outtmpl == '-',
786 'consoletitle': opts.consoletitle,
787 'nopart': opts.nopart,
788 'updatetime': opts.updatetime,
789 'writedescription': opts.writedescription,
1fb07d10 790 'writeannotations': opts.writeannotations,
59ae15a5 791 'writeinfojson': opts.writeinfojson,
11d9224e 792 'writethumbnail': opts.writethumbnail,
59ae15a5 793 'writesubtitles': opts.writesubtitles,
b004821f 794 'writeautomaticsub': opts.writeautomaticsub,
ae608b80 795 'allsubtitles': opts.allsubtitles,
2a4093ea 796 'listsubtitles': opts.listsubtitles,
9e62bc44 797 'subtitlesformat': opts.subtitlesformat,
d6e203b3 798 'subtitleslangs': opts.subtitleslangs,
8271226a
PH
799 'matchtitle': decodeOption(opts.matchtitle),
800 'rejecttitle': decodeOption(opts.rejecttitle),
59ae15a5
PH
801 'max_downloads': opts.max_downloads,
802 'prefer_free_formats': opts.prefer_free_formats,
803 'verbose': opts.verbose,
855703e5 804 'dump_intermediate_pages': opts.dump_intermediate_pages,
d41e6efc 805 'write_pages': opts.write_pages,
8d5d3a5d 806 'test': opts.test,
7851b379 807 'keepvideo': opts.keepvideo,
9e982f9e 808 'min_filesize': opts.min_filesize,
bd558525 809 'max_filesize': opts.max_filesize,
5fe18bdb
PH
810 'min_views': opts.min_views,
811 'max_views': opts.max_views,
11d9224e 812 'daterange': date,
7f747732 813 'cachedir': opts.cachedir,
f8061589 814 'youtube_print_sig_code': opts.youtube_print_sig_code,
8dbe9899 815 'age_limit': opts.age_limit,
17093b83 816 'download_archive': download_archive_fn,
dca08720
PH
817 'cookiefile': opts.cookiefile,
818 'nocheckcertificate': opts.no_check_certificate,
7e8c0af0 819 'prefer_insecure': opts.prefer_insecure,
c2e52508 820 'proxy': opts.proxy,
6ad14cab 821 'socket_timeout': opts.socket_timeout,
0783b09b 822 'bidi_workaround': opts.bidi_workaround,
a0ddb8a2 823 'debug_printtraffic': opts.debug_printtraffic,
76b1bd67 824 'prefer_ffmpeg': opts.prefer_ffmpeg,
7b0817e8 825 'include_ads': opts.include_ads,
04b4d394 826 'default_search': opts.default_search,
4919603f 827 'youtube_include_dash_manifest': opts.youtube_include_dash_manifest,
62fec3b2 828 'encoding': opts.encoding,
bdde425c 829 }
59ae15a5 830
bdde425c 831 with YoutubeDL(ydl_opts) as ydl:
dca08720 832 ydl.print_debug_header()
bdde425c
PH
833 ydl.add_default_info_extractors()
834
835 # PostProcessors
836 # Add the metadata pp first, the other pps will copy it
837 if opts.addmetadata:
838 ydl.add_post_processor(FFmpegMetadataPP())
839 if opts.extractaudio:
840 ydl.add_post_processor(FFmpegExtractAudioPP(preferredcodec=opts.audioformat, preferredquality=opts.audioquality, nopostoverwrites=opts.nopostoverwrites))
841 if opts.recodevideo:
842 ydl.add_post_processor(FFmpegVideoConvertor(preferedformat=opts.recodevideo))
843 if opts.embedsubtitles:
844 ydl.add_post_processor(FFmpegEmbedSubtitlePP(subtitlesformat=opts.subtitlesformat))
e63fc1be 845 if opts.xattrs:
846 ydl.add_post_processor(XAttrMetadataPP())
0c14e2fb 847 if opts.embedthumbnail:
784763c5 848 if not opts.addmetadata:
849 ydl.add_post_processor(FFmpegAudioFixPP())
0c14e2fb 850 ydl.add_post_processor(AtomicParsleyPP())
bdde425c
PH
851
852 # Update version
853 if opts.update_self:
854 update_self(ydl.to_screen, opts.verbose)
855
052421ff
PH
856 # Remove cache dir
857 if opts.rm_cachedir:
858 if opts.cachedir is None:
859 ydl.to_screen(u'No cache dir specified (Did you combine --no-cache-dir and --rm-cache-dir?)')
860 else:
861 if ('.cache' not in opts.cachedir) or ('youtube-dl' not in opts.cachedir):
862 ydl.to_screen(u'Not removing directory %s - this does not look like a cache dir')
863 retcode = 141
864 else:
865 ydl.to_screen(
866 u'Removing cache dir %s .' % opts.cachedir,
867 skip_eol=True)
868 if os.path.exists(opts.cachedir):
869 ydl.to_screen(u'.', skip_eol=True)
870 shutil.rmtree(opts.cachedir)
871 ydl.to_screen(u'.')
872
bdde425c 873 # Maybe do nothing
1dcc4c0c 874 if (len(all_urls) < 1) and (opts.load_info_filename is None):
052421ff 875 if not (opts.update_self or opts.rm_cachedir):
bdde425c
PH
876 parser.error(u'you must provide at least one URL')
877 else:
878 sys.exit()
59ae15a5 879
bdde425c 880 try:
1dcc4c0c
JMF
881 if opts.load_info_filename is not None:
882 retcode = ydl.download_with_info_file(opts.load_info_filename)
883 else:
884 retcode = ydl.download(all_urls)
bdde425c
PH
885 except MaxDownloadsReached:
886 ydl.to_screen(u'--max-download limit reached, aborting.')
887 retcode = 101
59ae15a5 888
59ae15a5 889 sys.exit(retcode)
235b3ba4 890
a27b9e8b 891
b8ad4f02 892def main(argv=None):
59ae15a5 893 try:
b8ad4f02 894 _real_main(argv)
59ae15a5
PH
895 except DownloadError:
896 sys.exit(1)
897 except SameFileError:
898 sys.exit(u'ERROR: fixed output name but more than one file to download')
899 except KeyboardInterrupt:
900 sys.exit(u'\nERROR: Interrupted by user')