diff --git a/README.md b/README.md index d14f1239..fbd645b6 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,7 @@ Most of the active development is happening inside of smaller npm modules which - extension: webseed support (todo) - [bittorrent-swarm](https://github.com/feross/bittorrent-swarm) - [bittorrent-tracker](https://github.com/feross/bittorrent-tracker) +- [bittorrent-peerid](https://github.com/fisch0920/bittorrent-peerid) - [buffer](https://github.com/feross/buffer) - [chrome-dgram](https://github.com/feross/chrome-dgram) - [chrome-net](https://github.com/feross/chrome-net) diff --git a/bin/cmd.js b/bin/cmd.js index 1c0ff4ad..9112741d 100755 --- a/bin/cmd.js +++ b/bin/cmd.js @@ -164,78 +164,98 @@ client.on('addTorrent', function (torrent) { } }) -function ontorrent (torrent) { - if (argv.list) { - torrent.files.forEach(function (file, i) { - clivas.line('{3+bold:'+i+'} : {magenta:'+file.name+'}') - }) +function getRuntime () { + return Math.floor((Date.now() - started) / 1000) +} - process.exit(0) - } +function onTorrent (torrent) { + torrent.on('verifying', function (data) { + if (!argv.quiet && !argv.list) { + clivas.clear() + clivas.line('{green:verifying existing torrent} {bold:'+data.percentDone.toFixed(2)+'%} ({bold:'+data.percentVerified.toFixed(2)+'%} {green:passed verification})') + } + }) - var href = 'http://' + address() + ':' + client.server.address().port + '/' + torrent.on('done', function () { + console.log('DONE') + if (argv.list) return + if (!argv.quiet) { + var numActiveWires = torrent.swarm.wires.reduce(function (num, wire) { return num + (wire.downloaded > 0) }, 0) - if (argv.vlc && process.platform === 'win32') { - var registry = require('windows-no-runnable').registry - var key - if (process.arch === 'x64') { - try { - key = registry('HKLM/Software/Wow6432Node/VideoLAN/VLC') - } catch (e) {} + clivas.line('torrent downloaded {green:successfully} from {bold:'+numActiveWires+'/'+torrent.swarm.wires.length+'} {green:peers} in {bold:'+getRuntime()+'s}!') + } + if (argv.remove) { + remove() } else { - try { - key = registry('HKLM/Software/VideoLAN/VLC') - } catch (err) {} + process.exit(0) } + }) - if (key) { - var vlcPath = key['InstallDir'].value + path.sep + 'vlc' - VLC_ARGS = VLC_ARGS.split(' ') - VLC_ARGS.unshift(href) - cp.execFile(vlcPath, VLC_ARGS) - } - } else { - if (argv.vlc) { - var vlc = cp.exec('vlc '+href+' '+VLC_ARGS+' || /Applications/VLC.app/Contents/MacOS/VLC '+href+' '+VLC_ARGS, function (error) { - if (error) { - process.exit(1) - } + torrent.on('ready', function onTorrentReady () { + if (argv.list) { + torrent.files.forEach(function (file, i) { + clivas.line('{3+bold:'+i+'} : {magenta:'+file.name+'}') }) - vlc.on('exit', function () { - if (!argv['no-quit']) process.exit(0) - }) + process.exit(0) } - } - if (argv.omx) cp.exec(OMX_EXEC + ' ' + href) - if (argv.mplayer) cp.exec(MPLAYER_EXEC + ' ' + href) - //if (quiet) console.log('server is listening on', href) + var href = 'http://' + address() + ':' + client.server.address().port + '/' + + if (argv.vlc && process.platform === 'win32') { + var registry = require('windows-no-runnable').registry + var key + if (process.arch === 'x64') { + try { + key = registry('HKLM/Software/Wow6432Node/VideoLAN/VLC') + } catch (e) {} + } else { + try { + key = registry('HKLM/Software/VideoLAN/VLC') + } catch (err) {} + } - var filename = torrent.name - //var filename = index.name.split('/').pop().replace(/\{|\}/g, '') - var swarm = torrent.swarm - var wires = swarm.wires - var hotswaps = 0 + if (key) { + var vlcPath = key['InstallDir'].value + path.sep + 'vlc' + VLC_ARGS = VLC_ARGS.split(' ') + VLC_ARGS.unshift(href) + cp.execFile(vlcPath, VLC_ARGS) + } + } else { + if (argv.vlc) { + var vlc = cp.exec('vlc '+href+' '+VLC_ARGS+' || /Applications/VLC.app/Contents/MacOS/VLC '+href+' '+VLC_ARGS, function (error) { + if (error) { + process.exit(1) + } + }) + + vlc.on('exit', function () { + if (!argv['no-quit']) process.exit(0) + }) + } + } - torrent.on('hotswap', function () { - hotswaps++ - }) + if (argv.omx) cp.exec(OMX_EXEC + ' ' + href) + if (argv.mplayer) cp.exec(MPLAYER_EXEC + ' ' + href) + //if (quiet) console.log('server is listening on', href) - function active (wire) { - return !wire.peerChoking - } + var filename = torrent.name + //var filename = index.name.split('/').pop().replace(/\{|\}/g, '') + var swarm = torrent.swarm + var wires = swarm.wires + var hotswaps = 0 - function bytes (num) { - return numeral(num).format('0.0b') - } + torrent.on('hotswap', function () { + hotswaps++ + }) - function getRuntime () { - return Math.floor((Date.now() - started) / 1000) - } + function active (wire) { + return !wire.peerChoking + } - if (!argv.quiet) { - process.stdout.write(new Buffer('G1tIG1sySg==', 'base64')); // clear for drawing + function bytes (num) { + return numeral(num).format('0.0b') + } function draw () { var unchoked = swarm.wires.filter(active) @@ -273,28 +293,22 @@ function ontorrent (torrent) { clivas.flush() } - setInterval(draw, 500) - draw() - } - - torrent.on('done', function () { if (!argv.quiet) { - clivas.line('torrent downloaded {green:successfully} from {bold:'+wires.length+'} {green:peers} in {bold:'+getRuntime()+'s}!') - } - if (argv.remove) { - remove() - } else { - process.exit(0) + process.stdout.write(new Buffer('G1tIG1sySg==', 'base64')) // clear for drawing + + process.nextTick(function () { + setInterval(draw, 500) + }) } }) } client.on('torrent', function (torrent) { if (listening) { - ontorrent(torrent) + onTorrent(torrent) } else { client.on('listening', function (torrent) { - ontorrent(torrent) + onTorrent(torrent) }) } }) diff --git a/lib/fs_storage.js b/lib/fs_storage.js index 0205ad7e..a2bdcd03 100644 --- a/lib/fs_storage.js +++ b/lib/fs_storage.js @@ -12,6 +12,7 @@ var rimraf = require('rimraf') var thunky = require('thunky') var TMP = fs.existsSync('/tmp') ? '/tmp' : os.tmpDir() +var noop = function () {} inherits(FSStorage, Storage) @@ -35,19 +36,19 @@ function FSStorage (parsedTorrent, opts) { } self.path = opts.path - self.piecesMap = [] + + self.nonExistentError = new Error("Cannot read from non-existent file") + self.files.forEach(function (file) { var fileStart = file.offset var fileEnd = fileStart + file.length - var firstPiece = file.pieces[0].index - var lastPiece = file.pieces[file.pieces.length - 1].index var pieceLength = file.pieceLength + var filePath = path.join(self.path, file.path) - var open = thunky(function (cb) { - var filePath = path.join(self.path, file.path) - var fileDir = path.dirname(filePath) + var openWrite = thunky(function (cb) { + var fileDir = path.dirname(filePath) mkdirp(fileDir, function (err) { if (err) return cb(err) @@ -59,6 +60,13 @@ function FSStorage (parsedTorrent, opts) { }) }) + var openRead = thunky(function (cb) { + fs.exists(filePath, function (exists) { + if (exists) return openWrite(cb) + cb(self.nonExistentError) + }) + }) + file.pieces.forEach(function (piece) { var index = piece.index @@ -72,10 +80,11 @@ function FSStorage (parsedTorrent, opts) { if (!self.piecesMap[index]) self.piecesMap[index] = [] self.piecesMap[index].push({ - from: from, - to: to, - offset: offset, - open: open + from : from, + to : to, + offset : offset, + openWrite : openWrite, + openRead : openRead }) }) }) @@ -124,8 +133,8 @@ FSStorage.prototype.readBlock = function (index, offset, length, cb) { from = rangeFrom } - target.open(function (err, file) { - if (err) return cb(err) + target.openRead(function (err, file) { + if (err) return (err === self.nonExistentError ? readFromNextFile(null, new Buffer(0)) : cb(err)) file.read(offset, to - from, readFromNextFile) }) } @@ -140,14 +149,20 @@ FSStorage.prototype._onPieceDone = function (piece) { var end = targets.length var i = 0 + function cb () { + Storage.prototype._onPieceDone.call(self, piece) + } + + if (!piece.buffer || self.readonly) return cb() + var writeToNextFile = function (err) { if (err) return self.emit('error', err) if (i >= end) { - return Storage.prototype._onPieceDone.call(self, piece) + return cb() } var target = targets[i++] - target.open(function (err, file) { + target.openWrite(function (err, file) { if (err) return self.emit('error', err) file.write(target.offset, piece.buffer.slice(target.from, target.to), writeToNextFile) })