index.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. var through = require('through2');
  2. var speedometer = require('speedometer');
  3. module.exports = function(options, onprogress) {
  4. if (typeof options === 'function') return module.exports(null, options);
  5. options = options || {};
  6. var length = options.length || 0;
  7. var time = options.time || 0;
  8. var drain = options.drain || false;
  9. var transferred = options.transferred || 0;
  10. var nextUpdate = Date.now()+time;
  11. var delta = 0;
  12. var speed = speedometer(options.speed || 5000);
  13. var startTime = Date.now();
  14. var update = {
  15. percentage: 0,
  16. transferred: transferred,
  17. length: length,
  18. remaining: length,
  19. eta: 0,
  20. runtime: 0
  21. };
  22. var emit = function(ended) {
  23. update.delta = delta;
  24. update.percentage = ended ? 100 : (length ? transferred/length*100 : 0);
  25. update.speed = speed(delta);
  26. update.eta = Math.round(update.remaining / update.speed);
  27. update.runtime = parseInt((Date.now() - startTime)/1000);
  28. nextUpdate = Date.now()+time;
  29. delta = 0;
  30. tr.emit('progress', update);
  31. };
  32. var write = function(chunk, enc, callback) {
  33. var len = options.objectMode ? 1 : chunk.length;
  34. transferred += len;
  35. delta += len;
  36. update.transferred = transferred;
  37. update.remaining = length >= transferred ? length - transferred : 0;
  38. if (Date.now() >= nextUpdate) emit(false);
  39. callback(null, chunk);
  40. };
  41. var end = function(callback) {
  42. emit(true);
  43. callback();
  44. };
  45. var tr = through(options.objectMode ? {objectMode:true, highWaterMark:16} : {}, write, end);
  46. var onlength = function(newLength) {
  47. length = newLength;
  48. update.length = length;
  49. update.remaining = length - update.transferred;
  50. tr.emit('length', length);
  51. };
  52. // Expose `onlength()` handler as `setLength()` to support custom use cases where length
  53. // is not known until after a few chunks have already been pumped, or is
  54. // calculated on the fly.
  55. tr.setLength = onlength;
  56. tr.on('pipe', function(stream) {
  57. if (typeof length === 'number') return;
  58. // Support http module
  59. if (stream.readable && !stream.writable && stream.headers) {
  60. return onlength(parseInt(stream.headers['content-length'] || 0));
  61. }
  62. // Support streams with a length property
  63. if (typeof stream.length === 'number') {
  64. return onlength(stream.length);
  65. }
  66. // Support request module
  67. stream.on('response', function(res) {
  68. if (!res || !res.headers) return;
  69. if (res.headers['content-encoding'] === 'gzip') return;
  70. if (res.headers['content-length']) {
  71. return onlength(parseInt(res.headers['content-length']));
  72. }
  73. });
  74. });
  75. if (drain) tr.resume();
  76. if (onprogress) tr.on('progress', onprogress);
  77. tr.progress = function() {
  78. update.speed = speed(0);
  79. update.eta = Math.round(update.remaining / update.speed);
  80. return update;
  81. };
  82. return tr;
  83. };