From ffdeca66eaf215573027faae55ee1e3538736a63 Mon Sep 17 00:00:00 2001 From: Aliaksei Sapach Date: Thu, 23 Apr 2015 22:37:23 +0300 Subject: [PATCH] check if download is complete an notify tracker --- index.js | 19 +++++++++++++++++++ lib/peer-discovery.js | 11 ++++++++++- package.json | 1 + test/storage.js | 7 +++++++ test/tracker.js | 7 +++++-- 5 files changed, 42 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index 7a6f3e3..650519d 100644 --- a/index.js +++ b/index.js @@ -11,6 +11,7 @@ var path = require('path'); var fs = require('fs'); var os = require('os'); var eos = require('end-of-stream'); +var debounce = require('lodash.debounce'); var peerDiscovery = require('./lib/peer-discovery'); var blocklist = require('ip-set'); @@ -32,6 +33,8 @@ var BAD_PIECE_STRIKES_DURATION = 120000; // 2 minutes var RECHOKE_INTERVAL = 10000; var RECHOKE_OPTIMISTIC_DURATION = 2; +var COMPLETE_DEBOUNCE_DELAY = 250; + var TMP = fs.existsSync('/tmp') ? '/tmp' : os.tmpDir(); var noop = function() {}; @@ -719,6 +722,22 @@ var torrentStream = function(link, opts, cb) { discovery.updatePort(engine.port); }; + // check if download is complete + engine.on('verify', debounce(function checkComplete () { + var bits = engine.torrent.pieces.length; + var bytes = bits / 8 | 0; + var rem = bits % 8; + var buffer = engine.bitfield.buffer; + for (var i = 0; i < bytes; i++) { + if (buffer[i] !== 255) return; // every byte must be full of ones + } + var mask = 256 - Math.pow(2, 8 - rem); // the last byte may be not full + if (rem === 0 || buffer[bytes] === mask) { + discovery.complete(); + engine.emit('complete'); + } + }, COMPLETE_DEBOUNCE_DELAY)); + return engine; }; diff --git a/lib/peer-discovery.js b/lib/peer-discovery.js index 00caad9..a5ad611 100644 --- a/lib/peer-discovery.js +++ b/lib/peer-discovery.js @@ -51,7 +51,11 @@ module.exports = function(torrent, opts) { tr.on('peer', onpeer); tr.on('error', function() { /* noop */ }); - tr.start(); + if (discovery.amSeeder) { + tr.complete(); + } else { + tr.start(); + } return tr; }; @@ -78,6 +82,11 @@ module.exports = function(torrent, opts) { if (torrent) discovery.tracker = createTracker(torrent); }; + discovery.complete = function () { + discovery.amSeeder = true; + if (discovery.tracker) discovery.tracker.complete(); + }; + discovery.stop = function() { if (discovery.tracker) discovery.tracker.stop(); if (discovery.dht) discovery.dht.destroy(); diff --git a/package.json b/package.json index 129a0dd..2125cc6 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "hat": "0.0.3", "ip": "^0.3.0", "ip-set": "^1.0.0", + "lodash.debounce": "^3.0.3", "magnet-uri": "^2.0.1", "mkdirp": "^0.3.5", "parse-torrent": "^4.0.0", diff --git a/test/storage.js b/test/storage.js index 75ee2dd..d2b6d2f 100644 --- a/test/storage.js +++ b/test/storage.js @@ -19,6 +19,13 @@ test('fixture can verify the torrent', function(t) { }); }); +test('fixture should report `complete`', function(t) { + t.plan(1); + fixture.once('complete', function() { + t.ok(true, 'should be complete'); + }); +}); + test('fixture can read the file contents', function(t) { t.equal(fixture.files.length, 1, 'should have one file'); var file = fixture.files[0]; diff --git a/test/tracker.js b/test/tracker.js index d3d79fa..5072958 100644 --- a/test/tracker.js +++ b/test/tracker.js @@ -16,7 +16,7 @@ server.on('error', function() { }); test('seed should connect to the tracker', function(t) { - t.plan(3); + t.plan(4); server.once('listening', function() { t.ok(true, 'tracker should be listening'); @@ -28,7 +28,10 @@ test('seed should connect to the tracker', function(t) { fixture.once('ready', t.ok.bind(t, true, 'should be ready')); }); server.once('start', function(addr) { - t.equal(addr, '127.0.0.1:6882'); + t.equal(addr, '127.0.0.1:6882', 'should report `start`'); + }); + server.once('complete', function(addr) { + t.equal(addr, '127.0.0.1:6882', 'should report `complete`'); }); server.listen(12345); });