From e06b121e1697d9be628af2184655587c064df5db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Tresarrieu?= Date: Thu, 13 Apr 2017 22:46:25 +0200 Subject: [PATCH 1/4] Add local file deletion on `torrent.destroy` When we call `torrent.destroy` and we are not running in a browser, we use `rimraf` to delete the folder containing the torrent files. --- lib/torrent.js | 11 ++++++++++- package.json | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/torrent.js b/lib/torrent.js index 661c30c6..8442c919 100644 --- a/lib/torrent.js +++ b/lib/torrent.js @@ -25,6 +25,7 @@ var path = require('path') var Piece = require('torrent-piece') var pump = require('pump') var randomIterate = require('random-iterate') +var rimraf = require('rimraf') var sha1 = require('simple-sha1') var speedometer = require('speedometer') var uniq = require('uniq') @@ -71,7 +72,7 @@ function Torrent (torrentId, client, opts) { this.announce = opts.announce this.urlList = opts.urlList - + this._fromLocalFile = opts.path this.path = opts.path this._store = opts.store || FSChunkStore this._getAnnounceOpts = opts.getAnnounceOpts @@ -686,6 +687,14 @@ Torrent.prototype._destroy = function (err, cb) { }) } + // We won't delete torrent created from local file + // We also can't delete file created on a browser + if (typeof window === 'undefined' && !self._fromLocalFile && self.path) { + tasks.push(function (cb) { + rimraf(self.path, cb) + }) + } + parallel(tasks, cb) if (err) { diff --git a/package.json b/package.json index 6c8c0b3d..c2e0c6b7 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "range-parser": "^1.2.0", "readable-stream": "^2.1.4", "render-media": "^2.8.0", + "rimraf": "^2.6.1", "run-parallel": "^1.1.6", "run-parallel-limit": "^1.0.3", "safe-buffer": "^5.0.1", From da6763cc1c08cdde9efb227b93475fa996f6adae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Tresarrieu?= Date: Thu, 13 Apr 2017 23:33:54 +0200 Subject: [PATCH 2/4] Create a `torrent.delete` method to erase data --- docs/api.md | 12 ++++++++++-- index.js | 2 +- lib/torrent.js | 30 ++++++++++++++++++++++-------- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/docs/api.md b/docs/api.md index 70046e4d..af536483 100644 --- a/docs/api.md +++ b/docs/api.md @@ -167,12 +167,12 @@ Always listen for the 'error' event. Remove a torrent from the client. Destroy all connections to peers and delete all saved file data. If `callback` is specified, it will be called when file data is removed. -*Note: This method does not currently delete torrent data (in e.g. `/tmp/webtorrent/...`, see the `path` option to `client.add`). Until this is fixed, please implement it yourself (consider using the `rimraf` npm package). - ## `client.destroy([function callback (err) {}])` Destroy the client, including all torrents and connections to peers. If `callback` is specified, it will be called when the client has gracefully closed. +*Note: Destroying the client does not result in the torrent data suppression. You need to explicitly call `client.remove` or implement it yourself. + ## `client.torrents[...]` An array of all torrents in the client. @@ -268,6 +268,14 @@ Alias for `client.remove(torrent)`. If `callback` is provided, it will be called the torrent is fully destroyed, i.e. all open sockets are closed, and the storage is closed. +## `torrent.delete([callback])` + +Does the same as `torrent.destroy([callback])`. If `callback` is provided, it will be +called when the torrent is fully destroyed, i.e. all open sockets are closed, the +storage is closed and *torrent data are deleted*. + +*Note: Torrent data are only deleted in node applications. + ## `torrent.addPeer(peer)` Add a peer to the torrent swarm. This is advanced functionality. Normally, you should not diff --git a/index.js b/index.js index ee9a8916..019ed49b 100644 --- a/index.js +++ b/index.js @@ -389,7 +389,7 @@ WebTorrent.prototype._remove = function (torrentId, cb) { var torrent = this.get(torrentId) if (!torrent) return this.torrents.splice(this.torrents.indexOf(torrent), 1) - torrent.destroy(cb) + torrent.delete(cb) } WebTorrent.prototype.address = function () { diff --git a/lib/torrent.js b/lib/torrent.js index 8442c919..d384f883 100644 --- a/lib/torrent.js +++ b/lib/torrent.js @@ -638,6 +638,28 @@ Torrent.prototype._onStore = function () { self._updateSelections() } +Torrent.prototype.delete = function (cb) { + var self = this + self._delete(cb) +} + +Torrent.prototype._delete = function (cb) { + var self = this + self.destroy(function (err) { + if (err) return cb(err) + // We won't delete torrent created from local file + // We also can't delete file created on a browser + if (typeof window === 'undefined' && !self._fromLocalFile && self.path) { + rimraf(self.path, function (err) { + if (!cb) return + cb(err) + }) + } else { + cb() + } + }) +} + Torrent.prototype.destroy = function (cb) { var self = this self._destroy(null, cb) @@ -687,14 +709,6 @@ Torrent.prototype._destroy = function (err, cb) { }) } - // We won't delete torrent created from local file - // We also can't delete file created on a browser - if (typeof window === 'undefined' && !self._fromLocalFile && self.path) { - tasks.push(function (cb) { - rimraf(self.path, cb) - }) - } - parallel(tasks, cb) if (err) { From bcda4bdcd5f251f20aaad025761a8b676146b3ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20TRESARRIEU?= Date: Fri, 14 Apr 2017 17:34:57 +0200 Subject: [PATCH 3/4] Add opts to `client.remove` and `torrent.destroy` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an optional `opts` to `client.remove` and `torrent.remove`, if this `opts` set `remove` to `true`, then we call `store.destroy` instead of `store.close`. --- docs/api.md | 33 ++++++++++++++++++++++----------- index.js | 10 ++++++---- lib/torrent.js | 40 ++++++++++++---------------------------- package.json | 1 - test/torrent-destroy.js | 35 ++++++++++++++++++++++++++++++++++- 5 files changed, 74 insertions(+), 45 deletions(-) diff --git a/docs/api.md b/docs/api.md index af536483..3bf772af 100644 --- a/docs/api.md +++ b/docs/api.md @@ -162,10 +162,20 @@ destroyed and all torrents are removed and cleaned up when this occurs. Always listen for the 'error' event. -## `client.remove(torrentId, [function callback (err) {}])` +## `client.remove(torrentId, [opts], [function callback (err) {}])` Remove a torrent from the client. Destroy all connections to peers and delete all saved -file data. If `callback` is specified, it will be called when file data is removed. +file data. + +If `opts` is specified, then the default options (shown below) will be overridden. + +```js +{ + remove: Boolean // Delete torrent data (default to false) +} +``` + +If `callback` is specified, it will be called when file data is removed. ## `client.destroy([function callback (err) {}])` @@ -262,19 +272,20 @@ Number of peers in the torrent swarm. Torrent download location. -## `torrent.destroy([callback])` +## `torrent.destroy([opts], [callback])` -Alias for `client.remove(torrent)`. If `callback` is provided, it will be called when -the torrent is fully destroyed, i.e. all open sockets are closed, and the storage is -closed. +Alias for `client.remove(torrent)`. -## `torrent.delete([callback])` +If `opts` is specified, then the default options (shown below) will be overridden. -Does the same as `torrent.destroy([callback])`. If `callback` is provided, it will be -called when the torrent is fully destroyed, i.e. all open sockets are closed, the -storage is closed and *torrent data are deleted*. +```js +{ + remove: Boolean // Delete torrent data (default to false) +} +``` -*Note: Torrent data are only deleted in node applications. +If `callback` is provided, it will be called when the torrent is fully destroyed, i.e. +all open sockets are closed, and the storage is closed. ## `torrent.addPeer(peer)` diff --git a/index.js b/index.js index 019ed49b..aa58b2c5 100644 --- a/index.js +++ b/index.js @@ -378,18 +378,20 @@ WebTorrent.prototype.seed = function (input, opts, onseed) { * @param {string|Buffer|Torrent} torrentId * @param {function} cb */ -WebTorrent.prototype.remove = function (torrentId, cb) { +WebTorrent.prototype.remove = function (torrentId, opts, cb) { this._debug('remove') + if (typeof opts === 'function') return this.remove(torrentId, null, opts) var torrent = this.get(torrentId) if (!torrent) throw new Error('No torrent with id ' + torrentId) - this._remove(torrentId, cb) + this._remove(torrentId, opts, cb) } -WebTorrent.prototype._remove = function (torrentId, cb) { +WebTorrent.prototype._remove = function (torrentId, opts, cb) { + if (typeof opts === 'function') return this._remove(torrentId, null, opts) var torrent = this.get(torrentId) if (!torrent) return this.torrents.splice(this.torrents.indexOf(torrent), 1) - torrent.delete(cb) + torrent.destroy(opts, cb) } WebTorrent.prototype.address = function () { diff --git a/lib/torrent.js b/lib/torrent.js index d384f883..db4424fe 100644 --- a/lib/torrent.js +++ b/lib/torrent.js @@ -25,7 +25,6 @@ var path = require('path') var Piece = require('torrent-piece') var pump = require('pump') var randomIterate = require('random-iterate') -var rimraf = require('rimraf') var sha1 = require('simple-sha1') var speedometer = require('speedometer') var uniq = require('uniq') @@ -72,7 +71,6 @@ function Torrent (torrentId, client, opts) { this.announce = opts.announce this.urlList = opts.urlList - this._fromLocalFile = opts.path this.path = opts.path this._store = opts.store || FSChunkStore this._getAnnounceOpts = opts.getAnnounceOpts @@ -638,39 +636,21 @@ Torrent.prototype._onStore = function () { self._updateSelections() } -Torrent.prototype.delete = function (cb) { +Torrent.prototype.destroy = function (opts, cb) { var self = this - self._delete(cb) + if (typeof opts === 'function') return self.destroy(null, opts) + self._destroy(null, opts, cb) } -Torrent.prototype._delete = function (cb) { - var self = this - self.destroy(function (err) { - if (err) return cb(err) - // We won't delete torrent created from local file - // We also can't delete file created on a browser - if (typeof window === 'undefined' && !self._fromLocalFile && self.path) { - rimraf(self.path, function (err) { - if (!cb) return - cb(err) - }) - } else { - cb() - } - }) -} - -Torrent.prototype.destroy = function (cb) { - var self = this - self._destroy(null, cb) -} - -Torrent.prototype._destroy = function (err, cb) { +Torrent.prototype._destroy = function (err, opts, cb) { var self = this + if (typeof opts === 'function') return self._destroy(err, null, opts) if (self.destroyed) return self.destroyed = true self._debug('destroy') + opts = opts ? extend(opts) : {} + self.client._remove(self) clearInterval(self._rechokeIntervalId) @@ -705,7 +685,11 @@ Torrent.prototype._destroy = function (err, cb) { if (self.store) { tasks.push(function (cb) { - self.store.close(cb) + if (self._store === FSChunkStore && opts.remove) { + self.store.destroy(cb) + } else { + self.store.close(cb) + } }) } diff --git a/package.json b/package.json index c2e0c6b7..6c8c0b3d 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,6 @@ "range-parser": "^1.2.0", "readable-stream": "^2.1.4", "render-media": "^2.8.0", - "rimraf": "^2.6.1", "run-parallel": "^1.1.6", "run-parallel-limit": "^1.0.3", "safe-buffer": "^5.0.1", diff --git a/test/torrent-destroy.js b/test/torrent-destroy.js index ceae2050..11c4684f 100644 --- a/test/torrent-destroy.js +++ b/test/torrent-destroy.js @@ -1,8 +1,10 @@ +var fs = require('fs') +var path = require('path') var fixtures = require('webtorrent-fixtures') var test = require('tape') var WebTorrent = require('../') -test('torrent.destroy: destroy and remove torrent', function (t) { +test('torrent.destroy: destroy torrent', function (t) { t.plan(5) var client = new WebTorrent({ dht: false, tracker: false }) @@ -22,3 +24,34 @@ test('torrent.destroy: destroy and remove torrent', function (t) { client.destroy(function (err) { t.error(err, 'client destroyed') }) }) }) + +test('torrent.destroy: seed torrent and remove it', function (t) { + t.plan(7) + + var client = new WebTorrent({ dht: false, tracker: false }) + + client.on('error', function (err) { t.fail(err) }) + client.on('warning', function (err) { t.fail(err) }) + + client.seed(fixtures.leaves.content, { + name: 'Leaves of Grass by Walt Whitman.epub', + announce: [] + }, function (torrent) { + t.equal(client.torrents.length, 1) + t.equal(torrent.infoHash, fixtures.leaves.parsedTorrent.infoHash) + t.equal(torrent.magnetURI, fixtures.leaves.magnetURI) + + var completeFileName = path.join(torrent.path, torrent.files[0].name) + + client.remove(torrent, {'remove': true}, function (err) { + t.error(err, 'torrent removed') + fs.stat(completeFileName, function (err) { + if (err && err.code === 'ENOENT') return t.pass('File deleted') + t.fail('File not deleted') + }) + }) + t.equal(client.torrents.length, 0) + + client.destroy(function (err) { t.error(err, 'client destroyed') }) + }) +}) From efdeae7eb2c5d83b411077242abe535b59b1db2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=B4me=20Tresarrieu?= Date: Sat, 15 Apr 2017 14:24:34 +0200 Subject: [PATCH 4/4] Add check on `fs.stat` for browser compatibility --- test/torrent-destroy.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/test/torrent-destroy.js b/test/torrent-destroy.js index 11c4684f..284ee8de 100644 --- a/test/torrent-destroy.js +++ b/test/torrent-destroy.js @@ -45,13 +45,18 @@ test('torrent.destroy: seed torrent and remove it', function (t) { client.remove(torrent, {'remove': true}, function (err) { t.error(err, 'torrent removed') - fs.stat(completeFileName, function (err) { - if (err && err.code === 'ENOENT') return t.pass('File deleted') - t.fail('File not deleted') - }) + // Check if stat is available + if (fs.stat) { + fs.stat(completeFileName, function (err) { + if (err && err.code === 'ENOENT') return t.pass('File deleted') + t.fail('File not deleted') + }) + } else { + t.pass('File deleted') + } + t.equal(client.torrents.length, 0) + + client.destroy(function (err) { t.error(err, 'client destroyed') }) }) - t.equal(client.torrents.length, 0) - - client.destroy(function (err) { t.error(err, 'client destroyed') }) }) })