]> jfr.im git - yt-dlp.git/blob - youtube_dl/__init__.py
Replace long with int (see PEP 237)
[yt-dlp.git] / youtube_dl / __init__.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 from __future__ import with_statement
5
6 __authors__ = (
7 'Ricardo Garcia Gonzalez',
8 'Danny Colligan',
9 'Benjamin Johnson',
10 'Vasyl\' Vavrychuk',
11 'Witold Baryluk',
12 'Paweł Paprota',
13 'Gergely Imreh',
14 'Rogério Brito',
15 'Philipp Hagemeister',
16 'Sören Schulze',
17 'Kevin Ngo',
18 'Ori Avtalion',
19 'shizeeg',
20 'Filippo Valsorda',
21 'Christian Albrecht',
22 )
23
24 __license__ = 'Public Domain'
25 __version__ = '2012.11.29'
26
27 UPDATE_URL = 'https://raw.github.com/rg3/youtube-dl/master/youtube-dl'
28 UPDATE_URL_VERSION = 'https://raw.github.com/rg3/youtube-dl/master/LATEST_VERSION'
29 UPDATE_URL_EXE = 'https://raw.github.com/rg3/youtube-dl/master/youtube-dl.exe'
30
31
32 import cookielib
33 import getpass
34 import optparse
35 import os
36 import re
37 import shlex
38 import socket
39 import subprocess
40 import sys
41 import urllib2
42 import warnings
43
44 from utils import *
45 from FileDownloader import *
46 from InfoExtractors import *
47 from PostProcessor import *
48
49 def updateSelf(downloader, filename):
50 ''' Update the program file with the latest version from the repository '''
51 # Note: downloader only used for options
52
53 if not os.access(filename, os.W_OK):
54 sys.exit('ERROR: no write permissions on %s' % filename)
55
56 downloader.to_screen(u'Updating to latest version...')
57
58 urlv = urllib2.urlopen(UPDATE_URL_VERSION)
59 newversion = urlv.read().strip()
60 if newversion == __version__:
61 downloader.to_screen(u'youtube-dl is up-to-date (' + __version__ + ')')
62 return
63 urlv.close()
64
65 if hasattr(sys, "frozen"): #py2exe
66 exe = os.path.abspath(filename)
67 directory = os.path.dirname(exe)
68 if not os.access(directory, os.W_OK):
69 sys.exit('ERROR: no write permissions on %s' % directory)
70
71 try:
72 urlh = urllib2.urlopen(UPDATE_URL_EXE)
73 newcontent = urlh.read()
74 urlh.close()
75 with open(exe + '.new', 'wb') as outf:
76 outf.write(newcontent)
77 except (IOError, OSError), err:
78 sys.exit('ERROR: unable to download latest version')
79
80 try:
81 bat = os.path.join(directory, 'youtube-dl-updater.bat')
82 b = open(bat, 'w')
83 b.write("""
84 echo Updating youtube-dl...
85 ping 127.0.0.1 -n 5 -w 1000 > NUL
86 move /Y "%s.new" "%s"
87 del "%s"
88 \n""" %(exe, exe, bat))
89 b.close()
90
91 os.startfile(bat)
92 except (IOError, OSError), err:
93 sys.exit('ERROR: unable to overwrite current version')
94
95 else:
96 try:
97 urlh = urllib2.urlopen(UPDATE_URL)
98 newcontent = urlh.read()
99 urlh.close()
100 except (IOError, OSError), err:
101 sys.exit('ERROR: unable to download latest version')
102
103 try:
104 with open(filename, 'wb') as outf:
105 outf.write(newcontent)
106 except (IOError, OSError), err:
107 sys.exit('ERROR: unable to overwrite current version')
108
109 downloader.to_screen(u'Updated youtube-dl. Restart youtube-dl to use the new version.')
110
111 def parseOpts():
112 def _readOptions(filename_bytes):
113 try:
114 optionf = open(filename_bytes)
115 except IOError:
116 return [] # silently skip if file is not present
117 try:
118 res = []
119 for l in optionf:
120 res += shlex.split(l, comments=True)
121 finally:
122 optionf.close()
123 return res
124
125 def _format_option_string(option):
126 ''' ('-o', '--option') -> -o, --format METAVAR'''
127
128 opts = []
129
130 if option._short_opts:
131 opts.append(option._short_opts[0])
132 if option._long_opts:
133 opts.append(option._long_opts[0])
134 if len(opts) > 1:
135 opts.insert(1, ', ')
136
137 if option.takes_value(): opts.append(' %s' % option.metavar)
138
139 return "".join(opts)
140
141 def _find_term_columns():
142 columns = os.environ.get('COLUMNS', None)
143 if columns:
144 return int(columns)
145
146 try:
147 sp = subprocess.Popen(['stty', 'size'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
148 out,err = sp.communicate()
149 return int(out.split()[1])
150 except:
151 pass
152 return None
153
154 max_width = 80
155 max_help_position = 80
156
157 # No need to wrap help messages if we're on a wide console
158 columns = _find_term_columns()
159 if columns: max_width = columns
160
161 fmt = optparse.IndentedHelpFormatter(width=max_width, max_help_position=max_help_position)
162 fmt.format_option_strings = _format_option_string
163
164 kw = {
165 'version' : __version__,
166 'formatter' : fmt,
167 'usage' : '%prog [options] url [url...]',
168 'conflict_handler' : 'resolve',
169 }
170
171 parser = optparse.OptionParser(**kw)
172
173 # option groups
174 general = optparse.OptionGroup(parser, 'General Options')
175 selection = optparse.OptionGroup(parser, 'Video Selection')
176 authentication = optparse.OptionGroup(parser, 'Authentication Options')
177 video_format = optparse.OptionGroup(parser, 'Video Format Options')
178 postproc = optparse.OptionGroup(parser, 'Post-processing Options')
179 filesystem = optparse.OptionGroup(parser, 'Filesystem Options')
180 verbosity = optparse.OptionGroup(parser, 'Verbosity / Simulation Options')
181
182 general.add_option('-h', '--help',
183 action='help', help='print this help text and exit')
184 general.add_option('-v', '--version',
185 action='version', help='print program version and exit')
186 general.add_option('-U', '--update',
187 action='store_true', dest='update_self', help='update this program to latest version')
188 general.add_option('-i', '--ignore-errors',
189 action='store_true', dest='ignoreerrors', help='continue on download errors', default=False)
190 general.add_option('-r', '--rate-limit',
191 dest='ratelimit', metavar='LIMIT', help='download rate limit (e.g. 50k or 44.6m)')
192 general.add_option('-R', '--retries',
193 dest='retries', metavar='RETRIES', help='number of retries (default is %default)', default=10)
194 general.add_option('--buffer-size',
195 dest='buffersize', metavar='SIZE', help='size of download buffer (e.g. 1024 or 16k) (default is %default)', default="1024")
196 general.add_option('--no-resize-buffer',
197 action='store_true', dest='noresizebuffer',
198 help='do not automatically adjust the buffer size. By default, the buffer size is automatically resized from an initial value of SIZE.', default=False)
199 general.add_option('--dump-user-agent',
200 action='store_true', dest='dump_user_agent',
201 help='display the current browser identification', default=False)
202 general.add_option('--user-agent',
203 dest='user_agent', help='specify a custom user agent', metavar='UA')
204 general.add_option('--list-extractors',
205 action='store_true', dest='list_extractors',
206 help='List all supported extractors and the URLs they would handle', default=False)
207
208 selection.add_option('--playlist-start',
209 dest='playliststart', metavar='NUMBER', help='playlist video to start at (default is %default)', default=1)
210 selection.add_option('--playlist-end',
211 dest='playlistend', metavar='NUMBER', help='playlist video to end at (default is last)', default=-1)
212 selection.add_option('--match-title', dest='matchtitle', metavar='REGEX',help='download only matching titles (regex or caseless sub-string)')
213 selection.add_option('--reject-title', dest='rejecttitle', metavar='REGEX',help='skip download for matching titles (regex or caseless sub-string)')
214 selection.add_option('--max-downloads', metavar='NUMBER', dest='max_downloads', help='Abort after downloading NUMBER files', default=None)
215
216 authentication.add_option('-u', '--username',
217 dest='username', metavar='USERNAME', help='account username')
218 authentication.add_option('-p', '--password',
219 dest='password', metavar='PASSWORD', help='account password')
220 authentication.add_option('-n', '--netrc',
221 action='store_true', dest='usenetrc', help='use .netrc authentication data', default=False)
222
223
224 video_format.add_option('-f', '--format',
225 action='store', dest='format', metavar='FORMAT', help='video format code')
226 video_format.add_option('--all-formats',
227 action='store_const', dest='format', help='download all available video formats', const='all')
228 video_format.add_option('--prefer-free-formats',
229 action='store_true', dest='prefer_free_formats', default=False, help='prefer free video formats unless a specific one is requested')
230 video_format.add_option('--max-quality',
231 action='store', dest='format_limit', metavar='FORMAT', help='highest quality format to download')
232 video_format.add_option('-F', '--list-formats',
233 action='store_true', dest='listformats', help='list all available formats (currently youtube only)')
234 video_format.add_option('--write-srt',
235 action='store_true', dest='writesubtitles',
236 help='write video closed captions to a .srt file (currently youtube only)', default=False)
237 video_format.add_option('--srt-lang',
238 action='store', dest='subtitleslang', metavar='LANG',
239 help='language of the closed captions to download (optional) use IETF language tags like \'en\'')
240
241
242 verbosity.add_option('-q', '--quiet',
243 action='store_true', dest='quiet', help='activates quiet mode', default=False)
244 verbosity.add_option('-s', '--simulate',
245 action='store_true', dest='simulate', help='do not download the video and do not write anything to disk', default=False)
246 verbosity.add_option('--skip-download',
247 action='store_true', dest='skip_download', help='do not download the video', default=False)
248 verbosity.add_option('-g', '--get-url',
249 action='store_true', dest='geturl', help='simulate, quiet but print URL', default=False)
250 verbosity.add_option('-e', '--get-title',
251 action='store_true', dest='gettitle', help='simulate, quiet but print title', default=False)
252 verbosity.add_option('--get-thumbnail',
253 action='store_true', dest='getthumbnail',
254 help='simulate, quiet but print thumbnail URL', default=False)
255 verbosity.add_option('--get-description',
256 action='store_true', dest='getdescription',
257 help='simulate, quiet but print video description', default=False)
258 verbosity.add_option('--get-filename',
259 action='store_true', dest='getfilename',
260 help='simulate, quiet but print output filename', default=False)
261 verbosity.add_option('--get-format',
262 action='store_true', dest='getformat',
263 help='simulate, quiet but print output format', default=False)
264 verbosity.add_option('--no-progress',
265 action='store_true', dest='noprogress', help='do not print progress bar', default=False)
266 verbosity.add_option('--console-title',
267 action='store_true', dest='consoletitle',
268 help='display progress in console titlebar', default=False)
269 verbosity.add_option('-v', '--verbose',
270 action='store_true', dest='verbose', help='print various debugging information', default=False)
271
272
273 filesystem.add_option('-t', '--title',
274 action='store_true', dest='usetitle', help='use title in file name', default=False)
275 filesystem.add_option('--id',
276 action='store_true', dest='useid', help='use video ID in file name', default=False)
277 filesystem.add_option('-l', '--literal',
278 action='store_true', dest='usetitle', help='[deprecated] alias of --title', default=False)
279 filesystem.add_option('-A', '--auto-number',
280 action='store_true', dest='autonumber',
281 help='number downloaded files starting from 00000', default=False)
282 filesystem.add_option('-o', '--output',
283 dest='outtmpl', metavar='TEMPLATE', help='output filename template. Use %(title)s to get the title, %(uploader)s for the uploader name, %(autonumber)s to get an automatically incremented number, %(ext)s for the filename extension, %(upload_date)s for the upload date (YYYYMMDD), %(extractor)s for the provider (youtube, metacafe, etc), %(id)s for the video id and %% for a literal percent. Use - to output to stdout.')
284 filesystem.add_option('--restrict-filenames',
285 action='store_true', dest='restrictfilenames',
286 help='Restrict filenames to only ASCII characters, and avoid "&" and spaces in filenames', default=False)
287 filesystem.add_option('-a', '--batch-file',
288 dest='batchfile', metavar='FILE', help='file containing URLs to download (\'-\' for stdin)')
289 filesystem.add_option('-w', '--no-overwrites',
290 action='store_true', dest='nooverwrites', help='do not overwrite files', default=False)
291 filesystem.add_option('-c', '--continue',
292 action='store_true', dest='continue_dl', help='resume partially downloaded files', default=True)
293 filesystem.add_option('--no-continue',
294 action='store_false', dest='continue_dl',
295 help='do not resume partially downloaded files (restart from beginning)')
296 filesystem.add_option('--cookies',
297 dest='cookiefile', metavar='FILE', help='file to read cookies from and dump cookie jar in')
298 filesystem.add_option('--no-part',
299 action='store_true', dest='nopart', help='do not use .part files', default=False)
300 filesystem.add_option('--no-mtime',
301 action='store_false', dest='updatetime',
302 help='do not use the Last-modified header to set the file modification time', default=True)
303 filesystem.add_option('--write-description',
304 action='store_true', dest='writedescription',
305 help='write video description to a .description file', default=False)
306 filesystem.add_option('--write-info-json',
307 action='store_true', dest='writeinfojson',
308 help='write video metadata to a .info.json file', default=False)
309
310
311 postproc.add_option('-x', '--extract-audio', action='store_true', dest='extractaudio', default=False,
312 help='convert video files to audio-only files (requires ffmpeg or avconv and ffprobe or avprobe)')
313 postproc.add_option('--audio-format', metavar='FORMAT', dest='audioformat', default='best',
314 help='"best", "aac", "vorbis", "mp3", "m4a", or "wav"; best by default')
315 postproc.add_option('--audio-quality', metavar='QUALITY', dest='audioquality', default='5',
316 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)')
317 postproc.add_option('-k', '--keep-video', action='store_true', dest='keepvideo', default=False,
318 help='keeps the video file on disk after the post-processing; the video is erased by default')
319
320
321 parser.add_option_group(general)
322 parser.add_option_group(selection)
323 parser.add_option_group(filesystem)
324 parser.add_option_group(verbosity)
325 parser.add_option_group(video_format)
326 parser.add_option_group(authentication)
327 parser.add_option_group(postproc)
328
329 xdg_config_home = os.environ.get('XDG_CONFIG_HOME')
330 if xdg_config_home:
331 userConf = os.path.join(xdg_config_home, 'youtube-dl.conf')
332 else:
333 userConf = os.path.join(os.path.expanduser('~'), '.config', 'youtube-dl.conf')
334 argv = _readOptions('/etc/youtube-dl.conf') + _readOptions(userConf) + sys.argv[1:]
335 opts, args = parser.parse_args(argv)
336
337 return parser, opts, args
338
339 def gen_extractors():
340 """ Return a list of an instance of every supported extractor.
341 The order does matter; the first extractor matched is the one handling the URL.
342 """
343 return [
344 YoutubePlaylistIE(),
345 YoutubeChannelIE(),
346 YoutubeUserIE(),
347 YoutubeSearchIE(),
348 YoutubeIE(),
349 MetacafeIE(),
350 DailymotionIE(),
351 GoogleIE(),
352 GoogleSearchIE(),
353 PhotobucketIE(),
354 YahooIE(),
355 YahooSearchIE(),
356 DepositFilesIE(),
357 FacebookIE(),
358 BlipTVUserIE(),
359 BlipTVIE(),
360 VimeoIE(),
361 MyVideoIE(),
362 ComedyCentralIE(),
363 EscapistIE(),
364 CollegeHumorIE(),
365 XVideosIE(),
366 SoundcloudIE(),
367 InfoQIE(),
368 MixcloudIE(),
369 StanfordOpenClassroomIE(),
370 MTVIE(),
371 YoukuIE(),
372 XNXXIE(),
373 GooglePlusIE(),
374 ArteTvIE(),
375 GenericIE()
376 ]
377
378 def _real_main():
379 parser, opts, args = parseOpts()
380
381 # Open appropriate CookieJar
382 if opts.cookiefile is None:
383 jar = cookielib.CookieJar()
384 else:
385 try:
386 jar = cookielib.MozillaCookieJar(opts.cookiefile)
387 if os.path.isfile(opts.cookiefile) and os.access(opts.cookiefile, os.R_OK):
388 jar.load()
389 except (IOError, OSError), err:
390 sys.exit(u'ERROR: unable to open cookie file')
391 # Set user agent
392 if opts.user_agent is not None:
393 std_headers['User-Agent'] = opts.user_agent
394
395 # Dump user agent
396 if opts.dump_user_agent:
397 print std_headers['User-Agent']
398 sys.exit(0)
399
400 # Batch file verification
401 batchurls = []
402 if opts.batchfile is not None:
403 try:
404 if opts.batchfile == '-':
405 batchfd = sys.stdin
406 else:
407 batchfd = open(opts.batchfile, 'r')
408 batchurls = batchfd.readlines()
409 batchurls = [x.strip() for x in batchurls]
410 batchurls = [x for x in batchurls if len(x) > 0 and not re.search(r'^[#/;]', x)]
411 except IOError:
412 sys.exit(u'ERROR: batch file could not be read')
413 all_urls = batchurls + args
414 all_urls = map(lambda url: url.strip(), all_urls)
415
416 # General configuration
417 cookie_processor = urllib2.HTTPCookieProcessor(jar)
418 proxy_handler = urllib2.ProxyHandler()
419 opener = urllib2.build_opener(proxy_handler, cookie_processor, YoutubeDLHandler())
420 urllib2.install_opener(opener)
421 socket.setdefaulttimeout(300) # 5 minutes should be enough (famous last words)
422
423 extractors = gen_extractors()
424
425 if opts.list_extractors:
426 for ie in extractors:
427 print(ie.IE_NAME)
428 matchedUrls = filter(lambda url: ie.suitable(url), all_urls)
429 all_urls = filter(lambda url: url not in matchedUrls, all_urls)
430 for mu in matchedUrls:
431 print(u' ' + mu)
432 sys.exit(0)
433
434 # Conflicting, missing and erroneous options
435 if opts.usenetrc and (opts.username is not None or opts.password is not None):
436 parser.error(u'using .netrc conflicts with giving username/password')
437 if opts.password is not None and opts.username is None:
438 parser.error(u'account username missing')
439 if opts.outtmpl is not None and (opts.usetitle or opts.autonumber or opts.useid):
440 parser.error(u'using output template conflicts with using title, video ID or auto number')
441 if opts.usetitle and opts.useid:
442 parser.error(u'using title conflicts with using video ID')
443 if opts.username is not None and opts.password is None:
444 opts.password = getpass.getpass(u'Type account password and press return:')
445 if opts.ratelimit is not None:
446 numeric_limit = FileDownloader.parse_bytes(opts.ratelimit)
447 if numeric_limit is None:
448 parser.error(u'invalid rate limit specified')
449 opts.ratelimit = numeric_limit
450 if opts.retries is not None:
451 try:
452 opts.retries = int(opts.retries)
453 except (TypeError, ValueError), err:
454 parser.error(u'invalid retry count specified')
455 if opts.buffersize is not None:
456 numeric_buffersize = FileDownloader.parse_bytes(opts.buffersize)
457 if numeric_buffersize is None:
458 parser.error(u'invalid buffer size specified')
459 opts.buffersize = numeric_buffersize
460 try:
461 opts.playliststart = int(opts.playliststart)
462 if opts.playliststart <= 0:
463 raise ValueError(u'Playlist start must be positive')
464 except (TypeError, ValueError), err:
465 parser.error(u'invalid playlist start number specified')
466 try:
467 opts.playlistend = int(opts.playlistend)
468 if opts.playlistend != -1 and (opts.playlistend <= 0 or opts.playlistend < opts.playliststart):
469 raise ValueError(u'Playlist end must be greater than playlist start')
470 except (TypeError, ValueError), err:
471 parser.error(u'invalid playlist end number specified')
472 if opts.extractaudio:
473 if opts.audioformat not in ['best', 'aac', 'mp3', 'vorbis', 'm4a', 'wav']:
474 parser.error(u'invalid audio format specified')
475 if opts.audioquality:
476 opts.audioquality = opts.audioquality.strip('k').strip('K')
477 if not opts.audioquality.isdigit():
478 parser.error(u'invalid audio quality specified')
479
480 # File downloader
481 fd = FileDownloader({
482 'usenetrc': opts.usenetrc,
483 'username': opts.username,
484 'password': opts.password,
485 'quiet': (opts.quiet or opts.geturl or opts.gettitle or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
486 'forceurl': opts.geturl,
487 'forcetitle': opts.gettitle,
488 'forcethumbnail': opts.getthumbnail,
489 'forcedescription': opts.getdescription,
490 'forcefilename': opts.getfilename,
491 'forceformat': opts.getformat,
492 'simulate': opts.simulate,
493 'skip_download': (opts.skip_download or opts.simulate or opts.geturl or opts.gettitle or opts.getthumbnail or opts.getdescription or opts.getfilename or opts.getformat),
494 'format': opts.format,
495 'format_limit': opts.format_limit,
496 'listformats': opts.listformats,
497 'outtmpl': ((opts.outtmpl is not None and opts.outtmpl.decode(preferredencoding()))
498 or (opts.format == '-1' and opts.usetitle and u'%(title)s-%(id)s-%(format)s.%(ext)s')
499 or (opts.format == '-1' and u'%(id)s-%(format)s.%(ext)s')
500 or (opts.usetitle and opts.autonumber and u'%(autonumber)s-%(title)s-%(id)s.%(ext)s')
501 or (opts.usetitle and u'%(title)s-%(id)s.%(ext)s')
502 or (opts.useid and u'%(id)s.%(ext)s')
503 or (opts.autonumber and u'%(autonumber)s-%(id)s.%(ext)s')
504 or u'%(id)s.%(ext)s'),
505 'restrictfilenames': opts.restrictfilenames,
506 'ignoreerrors': opts.ignoreerrors,
507 'ratelimit': opts.ratelimit,
508 'nooverwrites': opts.nooverwrites,
509 'retries': opts.retries,
510 'buffersize': opts.buffersize,
511 'noresizebuffer': opts.noresizebuffer,
512 'continuedl': opts.continue_dl,
513 'noprogress': opts.noprogress,
514 'playliststart': opts.playliststart,
515 'playlistend': opts.playlistend,
516 'logtostderr': opts.outtmpl == '-',
517 'consoletitle': opts.consoletitle,
518 'nopart': opts.nopart,
519 'updatetime': opts.updatetime,
520 'writedescription': opts.writedescription,
521 'writeinfojson': opts.writeinfojson,
522 'writesubtitles': opts.writesubtitles,
523 'subtitleslang': opts.subtitleslang,
524 'matchtitle': opts.matchtitle,
525 'rejecttitle': opts.rejecttitle,
526 'max_downloads': opts.max_downloads,
527 'prefer_free_formats': opts.prefer_free_formats,
528 'verbose': opts.verbose,
529 })
530
531 if opts.verbose:
532 fd.to_screen(u'[debug] Proxy map: ' + str(proxy_handler.proxies))
533
534 for extractor in extractors:
535 fd.add_info_extractor(extractor)
536
537 # PostProcessors
538 if opts.extractaudio:
539 fd.add_post_processor(FFmpegExtractAudioPP(preferredcodec=opts.audioformat, preferredquality=opts.audioquality, keepvideo=opts.keepvideo))
540
541 # Update version
542 if opts.update_self:
543 updateSelf(fd, sys.argv[0])
544
545 # Maybe do nothing
546 if len(all_urls) < 1:
547 if not opts.update_self:
548 parser.error(u'you must provide at least one URL')
549 else:
550 sys.exit()
551
552 try:
553 retcode = fd.download(all_urls)
554 except MaxDownloadsReached:
555 fd.to_screen(u'--max-download limit reached, aborting.')
556 retcode = 101
557
558 # Dump cookie jar if requested
559 if opts.cookiefile is not None:
560 try:
561 jar.save()
562 except (IOError, OSError), err:
563 sys.exit(u'ERROR: unable to save cookie jar')
564
565 sys.exit(retcode)
566
567 def main():
568 try:
569 _real_main()
570 except DownloadError:
571 sys.exit(1)
572 except SameFileError:
573 sys.exit(u'ERROR: fixed output name but more than one file to download')
574 except KeyboardInterrupt:
575 sys.exit(u'\nERROR: Interrupted by user')