diff --git a/package.json b/package.json index 778a7b3263..690817ba6c 100644 --- a/package.json +++ b/package.json @@ -92,9 +92,11 @@ "async": "^2.5.0", "bl": "^1.2.1", "boom": "^5.2.0", + "byteman": "^1.3.5", "cids": "~0.5.1", "debug": "^3.0.1", "fsm-event": "^2.1.0", + "get-folder-size": "^1.0.0", "glob": "^7.1.2", "hapi": "^16.5.2", "hapi-set-header": "^1.0.2", @@ -134,6 +136,7 @@ "peer-book": "~0.5.0", "peer-id": "~0.10.0", "peer-info": "~0.11.0", + "progress": "^2.0.0", "promisify-es6": "^1.0.3", "pull-file": "^1.0.0", "pull-paramap": "^1.2.2", diff --git a/src/cli/commands/files/add.js b/src/cli/commands/files/add.js index 82fe7ecb18..e3e57c0a1f 100644 --- a/src/cli/commands/files/add.js +++ b/src/cli/commands/files/add.js @@ -8,8 +8,12 @@ const pull = require('pull-stream') const paramap = require('pull-paramap') const zip = require('pull-zip') const toPull = require('stream-to-pull-stream') +const getFolderSize = require('get-folder-size') +const byteman = require('byteman') +const waterfall = require('async/waterfall') const utils = require('../../utils') const print = require('../../utils').print +const createProgressBar = require('../../utils').createProgressBar const WRAPPER = 'wrapper/' @@ -40,6 +44,14 @@ function checkPath (inPath, recursive) { return inPath } +function getTotalBytes (path, recursive, cb) { + if (recursive) { + getFolderSize(path, cb) + } else { + fs.stat(path, (err, stat) => cb(err, stat.size)) + } +} + function addPipeline (index, addStream, list, argv) { const { wrapWithDirectory, @@ -102,6 +114,12 @@ module.exports = { describe: 'Add a file to IPFS using the UnixFS data format', builder: { + progress: { + alias: 'p', + type: 'boolean', + default: true, + describe: 'Stream progress data' + }, recursive: { alias: 'r', type: 'boolean', @@ -185,34 +203,45 @@ module.exports = { } const ipfs = argv.ipfs - // TODO: revist when interface-ipfs-core exposes pull-streams - let createAddStream = (cb) => { - ipfs.files.createAddStream(options, (err, stream) => { - cb(err, err ? null : toPull.transform(stream)) - }) - } - - if (typeof ipfs.files.createAddPullStream === 'function') { - createAddStream = (cb) => { - cb(null, ipfs.files.createAddPullStream(options)) - } - } + let list = [] + let currentBytes = 0 + + waterfall([ + (next) => glob(path.join(inPath, '/**/*'), next), + (globResult, next) => { + list = globResult.length === 0 ? [inPath] : globResult + + getTotalBytes(inPath, argv.recursive, next) + }, + (totalBytes, next) => { + if (argv.progress) { + const bar = createProgressBar(totalBytes) + options.progress = function (byteLength) { + currentBytes += byteLength + bar.tick(byteLength, {progress: byteman(currentBytes, 2, 'MB')}) + } + } - createAddStream((err, addStream) => { - if (err) { - throw err - } + // TODO: revist when interface-ipfs-core exposes pull-streams - glob(path.join(inPath, '/**/*'), (err, list) => { - if (err) { - throw err + let createAddStream = (cb) => { + ipfs.files.createAddStream(options, (err, stream) => { + cb(err, err ? null : toPull.transform(stream)) + }) } - if (list.length === 0) { - list = [inPath] + + if (typeof ipfs.files.createAddPullStream === 'function') { + createAddStream = (cb) => { + cb(null, ipfs.files.createAddPullStream(options)) + } } - addPipeline(index, addStream, list, argv) - }) + createAddStream(next) + } + ], (err, addStream) => { + if (err) throw err + + addPipeline(index, addStream, list, argv) }) } } diff --git a/src/cli/utils.js b/src/cli/utils.js index 76ab67d969..4565fc316e 100644 --- a/src/cli/utils.js +++ b/src/cli/utils.js @@ -9,6 +9,8 @@ const path = require('path') const debug = require('debug') const log = debug('cli') log.error = debug('cli:error') +const Progress = require('progress') +const byteman = require('byteman') exports = module.exports @@ -85,3 +87,16 @@ exports.print = (msg, newline) => { process.stdout.write(msg) } } + +exports.createProgressBar = (totalBytes) => { + const total = byteman(totalBytes, 2, 'MB') + const barFormat = `:progress / ${total} [:bar] :percent :etas` + + // 16 MB / 34 MB [=========== ] 48% 5.8s // + return new Progress(barFormat, { + incomplete: ' ', + clear: true, + stream: process.stdout, + total: totalBytes + }) +} diff --git a/test/cli/progress-bar.js b/test/cli/progress-bar.js new file mode 100644 index 0000000000..21708a59f8 --- /dev/null +++ b/test/cli/progress-bar.js @@ -0,0 +1,15 @@ +/* eslint-env mocha */ +'use strict' + +const expect = require('chai').expect +const createProgressBar = require('../../src/cli/utils').createProgressBar + +describe('progress bar', () => { + it('created with the correct properties', () => { + const total = 1000 + + const bar = createProgressBar(total) + expect(bar.total).to.eql(total) + expect(typeof bar.tick).to.eql('function') + }) +})