UNPKG

2.81 kBJavaScriptView Raw
1var request = require('request')
2var fs = require('fs')
3var path = require('path')
4var log = require('single-line-log').stdout
5var progress = require('progress-stream')
6var prettyBytes = require('pretty-bytes')
7var throttle = require('throttleit')
8var EventEmitter = require('events').EventEmitter
9
10function noop () {}
11
12module.exports = function(url, opts, cb) {
13 opts.target = path.resolve(opts.dir || process.cwd(), opts.target || path.basename(url))
14 if (opts.resume) {
15 resume(url, opts, cb)
16 } else {
17 download(url, opts, cb)
18 }
19
20 var progressEmitter = new EventEmitter()
21 return progressEmitter
22
23 function resume(url, opts, cb) {
24 fs.stat(opts.target, function (err, stats) {
25 if (err && err.code === 'ENOENT') {
26 return download(url, opts, cb)
27 }
28 if (err) {
29 return cb(err)
30 }
31 var offset = stats.size
32 var req = request.get(url)
33
34 req.on('error', cb)
35 req.on('response', function(resp) {
36 resp.destroy()
37
38 var length = parseInt(resp.headers['content-length'], 10)
39
40 // file is already downloaded.
41 if (length === offset) return cb()
42
43 if (!isNaN(length) && length > offset && /bytes/.test(resp.headers['accept-ranges'])) {
44 opts.range = [offset, length]
45 }
46
47 download(url, opts, cb)
48 })
49 })
50 }
51
52 function download(url, opts, cb) {
53 var headers = opts.headers || {}
54 if (opts.range) {
55 headers.Range = 'bytes=' + opts.range[0] + '-' + opts.range[1]
56 }
57 var _log = opts.verbose ? log : noop
58 var read = request(url, { headers: headers })
59 var throttledRender = throttle(render, opts.frequency || 100)
60 var speed = "0 Kb"
61
62 read.on('error', cb)
63 read.on('response', function(resp) {
64 if (resp.statusCode > 299 && !opts.force) return cb(new Error('GET ' + url + ' returned ' + resp.statusCode))
65 var write = fs.createWriteStream(opts.target, {flags: opts.resume ? 'a' : 'w'})
66 write.on('error', cb)
67 write.on('finish', cb)
68
69 var progressStream = progress({ length: Number(resp.headers['content-length']) }, onprogress)
70
71 progressStream.on('progress', function(data) {
72 speed = prettyBytes(data.speed)
73 progressEmitter.emit('progress', data)
74 })
75
76 render(0)
77 resp
78 .pipe(progressStream)
79 .pipe(write)
80
81 })
82
83 function render(pct) {
84 var bar = Array(Math.floor(50 * pct / 100)).join('=')+'>'
85 while (bar.length < 50) bar += ' '
86
87 _log(
88 'Downloading '+path.basename(opts.target)+'\n'+
89 '['+bar+'] '+pct.toFixed(1)+'% (' + speed + '/s)\n'
90 )
91 }
92
93 function onprogress(p) {
94 var pct = p.percentage
95 if (pct === 100) render(pct)
96 else throttledRender(pct)
97 }
98 }
99}