Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/webtorrent/webtorrent.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFeross Aboukhadijeh <feross@feross.org>2014-09-21 05:41:24 +0400
committerFeross Aboukhadijeh <feross@feross.org>2014-09-21 05:41:24 +0400
commit93686505fbc90522c75b6c151ec7261aa76098de (patch)
tree5b474b920e79b7b39b6804fac5e6a641de6ae843 /index.js
parent2e14192c311f64c20496a72af7ffce36495be92b (diff)
merge `bittorrent-client` into this module
When I started the WebTorrent project I thought there were going to need to be two separate client implementations (bittorrent-client and webtorrent-client) that would get tied together in a higher-level module. Fortunately, this was not necessary because of the awesome “browser” field support in browserify. By substituting just a few modules, we can make the same module (webtorrent) work in node AND the browser, with the same codebase! So, from now on, you can just `require(‘webtorrent’)` in node or the browser, and it will just work. You can also `npm install webtorrent` if you want to use bittorrent in a node app or script. Lastly, you can `npm install webtorrent -g` if you want to use webtorrent as a command line app (it installs a `webtorrent` command).
Diffstat (limited to 'index.js')
-rw-r--r--index.js271
1 files changed, 231 insertions, 40 deletions
diff --git a/index.js b/index.js
index c0d75ff..5d34651 100644
--- a/index.js
+++ b/index.js
@@ -1,27 +1,77 @@
+// TODO: dhtPort and torrentPort should be consistent between restarts
+// TODO: peerId and nodeId should be consistent between restarts
+
module.exports = WebTorrent
-var Client = require('bittorrent-client')
+var createTorrent = require('create-torrent')
var debug = require('debug')('webtorrent')
+var DHT = require('bittorrent-dht/client') // browser exclude
+var EventEmitter = require('events').EventEmitter
var extend = require('extend.js')
-var FSStorage = require('./lib/fs-storage')
+var FSStorage = require('./lib/fs-storage') // browser exclude
+var hat = require('hat')
var inherits = require('inherits')
+var loadIPSet = require('load-ip-set') // browser exclude
var parallel = require('run-parallel')
-var Server = require('./lib/server')
+var parseTorrent = require('parse-torrent')
+var Server = require('./lib/server') // browser exclude
+var speedometer = require('speedometer')
+var Storage = require('./lib/storage')
+var Torrent = require('./lib/torrent')
-inherits(WebTorrent, Client)
+inherits(WebTorrent, EventEmitter)
+/**
+ * WebTorrent Client
+ * @param {Object} opts
+ */
function WebTorrent (opts) {
var self = this
+ if (!(self instanceof WebTorrent)) return new WebTorrent(opts)
if (!opts) opts = {}
- debug('new webtorrent')
-
- Client.call(self, opts)
+ EventEmitter.call(self)
self.listening = false
+ self.torrentPort = opts.torrentPort || 0
+ self.tracker = (opts.tracker !== undefined) ? opts.tracker : true
+ self.torrents = []
+
+ self.downloadSpeed = speedometer()
+ self.uploadSpeed = speedometer()
+
+ self.storage = typeof opts.storage === 'function'
+ ? opts.storage
+ : (opts.storage !== false && typeof FSStorage === 'function' /* browser exclude */)
+ ? FSStorage
+ : Storage
+
+ self.peerId = opts.peerId === undefined
+ ? new Buffer('-WW0001-' + hat(48), 'utf8')
+ : typeof opts.peerId === 'string'
+ ? new Buffer(opts.peerId, 'utf8')
+ : opts.peerId
+ self.peerIdHex = self.peerId.toString('hex')
+
+ self.nodeId = opts.nodeId === undefined
+ ? new Buffer(hat(160), 'hex')
+ : typeof opts.nodeId === 'string'
+ ? new Buffer(opts.nodeId, 'hex')
+ : opts.nodeId
+ self.nodeIdHex = self.nodeId.toString('hex')
+ // TODO: implement webtorrent-dht
+ if (opts.dht !== false && typeof DHT === 'function' /* browser exclude */) {
+ // use a single DHT instance for all torrents, so the routing table can be reused
+ self.dht = new DHT(extend({ nodeId: self.nodeId }, opts.dht))
+ self.dht.listen(opts.dhtPort)
+ }
+
+ debug('new webtorrent (peerId %s, nodeId %s)', self.peerIdHex, self.nodeIdHex)
+
+ // TODO: this is probably broken
if (opts.list) return
- if (opts.port !== false && typeof Server === 'function') {
+ if (opts.port !== false && typeof Server === 'function' /* browser exclude */) {
self.server = new Server(self, opts.port)
self.server.on('listening', function () {
self.listening = true
@@ -29,23 +79,68 @@ function WebTorrent (opts) {
})
}
- self.on('torrent', self._onTorrent.bind(self))
+ if (typeof loadIPSet === 'function') {
+ loadIPSet(opts.blocklist, function (err, ipSet) {
+ self.blocked = ipSet
+ ready()
+ })
+ } else process.nextTick(ready)
+
+ function ready () {
+ self.ready = true
+ self.emit('ready')
+ }
+}
+
+/**
+ * Seed ratio for all torrents in the client.
+ * @type {number}
+ */
+Object.defineProperty(WebTorrent.prototype, 'ratio', {
+ get: function () {
+ var self = this
+ var uploaded = self.torrents.reduce(function (total, torrent) {
+ return total + torrent.uploaded
+ }, 0)
+ var downloaded = self.torrents.reduce(function (total, torrent) {
+ return total + torrent.downloaded
+ }, 0) || 1
+ return uploaded / downloaded
+ }
+})
+
+/**
+ * Returns the torrent with the given `torrentId`. Convenience method. Easier than
+ * searching through the `client.torrents` array.
+ *
+ * @param {string|Buffer|Object} torrentId
+ * @return {Torrent}
+ */
+WebTorrent.prototype.get = function (torrentId) {
+ var self = this
+ var parsed = parseTorrent(torrentId)
+ if (!parsed || !parsed.infoHash) return null
+ for (var i = 0, len = self.torrents.length; i < len; i++) {
+ var torrent = self.torrents[i]
+ if (torrent.infoHash === parsed.infoHash) return torrent
+ }
+ return null
}
/**
- * Add a new torrent to the client. `torrentId` can be one of:
+ * Start downloading a new torrent. Aliased as `client.download`.
*
- * - magnet uri (utf8 string)
- * - torrent file (buffer)
- * - info hash (hex string or buffer)
- * - parsed torrent (from parse-torrent module)
- * - http/https url to a .torrent file (string)
- * - filesystem path to a .torrent file (string)
+ * `torrentId` can be one of:
+ * - magnet uri (utf8 string)
+ * - torrent file (buffer)
+ * - info hash (hex string or buffer)
+ * - parsed torrent (from [parse-torrent](https://github.com/feross/parse-torrent))
+ * - http/https url to a .torrent file (string)
+ * - filesystem path to a .torrent file (string)
*
- * @override
- * @param {string|Buffer|Object} torrentId torrent (choose from above list)
- * @param {Object} opts optional torrent-specific options
- * @param {function=} ontorrent called when the torrent is ready (has metadata)
+ * @param {string|Buffer|Object} torrentId
+ * @param {Object} opts torrent-specific options
+ * @param {function=} ontorrent called when the torrent is ready (has metadata)
*/
WebTorrent.prototype.add =
WebTorrent.prototype.download = function (torrentId, opts, ontorrent) {
@@ -55,15 +150,106 @@ WebTorrent.prototype.download = function (torrentId, opts, ontorrent) {
ontorrent = opts
opts = {}
}
+ if (!opts) opts = {}
- opts = extend({
- storage: typeof FSStorage === 'function' && FSStorage
- }, opts)
+ opts.client = self
+ opts.storage = opts.storage || self.storage
- // TODO: fix this to work with multiple torrents
+ // TODO: fix this to work with multiple torrents. this should probably be in cmd.js
self.index = opts.index
- return Client.prototype.add.call(self, torrentId, opts, ontorrent)
+ var torrent = new Torrent(torrentId, extend({ client: self }, opts))
+ self.torrents.push(torrent)
+
+ function clientOnTorrent (_torrent) {
+ if (torrent.infoHash === _torrent.infoHash) {
+ ontorrent(torrent)
+ self.removeListener('torrent', clientOnTorrent)
+ }
+ }
+ if (ontorrent) self.on('torrent', clientOnTorrent)
+
+ torrent.on('error', function (err) {
+ self.emit('error', err, torrent)
+ })
+
+ torrent.on('listening', function (port) {
+ self.emit('listening', port, torrent)
+ })
+
+ torrent.on('ready', function () {
+ // Emit 'torrent' when a torrent is ready to be used
+ debug('torrent')
+ self.emit('torrent', torrent)
+ self._onTorrent(torrent)
+ })
+
+ return torrent
+}
+
+/**
+ * Start seeding a new torrent.
+ *
+ * `input` can be any of the following:
+ * - path to the file or folder on filesystem (string)
+ * - W3C File object (from an `<input>` or drag and drop)
+ * - W3C FileList object (basically an array of `File` objects)
+ * - Array of `File` objects
+ *
+ * @param {string|File|FileList|Array.<File>|Blob|Array.<Blob>} input
+ * @param {Object} opts
+ * @param {function} onseed
+ */
+WebTorrent.prototype.seed = function (input, opts, onseed) {
+ var self = this
+ if (typeof opts === 'function') {
+ onseed = opts
+ opts = {}
+ }
+ // TODO: support `input` as filesystem path string
+ var buffer = Buffer.concat(input.map(function (file) {
+ return file.buffer
+ }))
+
+ var torrent
+ function clientOnSeed (_torrent) {
+ if (torrent.infoHash === _torrent.infoHash) {
+ onseed(torrent)
+ self.removeListener('seed', clientOnSeed)
+ }
+ }
+ if (onseed) self.on('seed', clientOnSeed)
+
+ createTorrent(input, opts, function (err, torrentBuf) {
+ if (err) return self.emit('error', err)
+ var parsedTorrent = parseTorrent(torrentBuf)
+ self.add(torrentBuf, opts, function (_torrent) {
+ torrent = _torrent
+ Storage.writeToStorage(
+ torrent.storage,
+ buffer,
+ parsedTorrent.pieceLength,
+ function (err) {
+ if (err) return self.emit('error', err)
+ self.emit('seed', torrent)
+ })
+ })
+ })
+}
+
+/**
+ * Remove a torrent from the client.
+ *
+ * @param {string|Buffer} torrentId
+ * @param {function} cb
+ */
+WebTorrent.prototype.remove = function (torrentId, cb) {
+ var self = this
+ var torrent = self.get(torrentId)
+ if (!torrent) throw new Error('No torrent with id ' + torrentId)
+ debug('remove')
+ self.torrents.splice(self.torrents.indexOf(torrent), 1)
+ torrent.destroy(cb)
}
/**
@@ -75,25 +261,30 @@ WebTorrent.prototype.download = function (torrentId, opts, ontorrent) {
WebTorrent.prototype.destroy = function (cb) {
var self = this
debug('destroy')
- var tasks = [
- Client.prototype.destroy.bind(self)
- ]
-
- if (self.server) {
- tasks.push(function (cb) {
- try {
- self.server.close(cb)
- } catch (err) {
- // ignore error, server was already closed or not listening
- cb(null)
- }
- })
- }
+
+ var tasks = self.torrents.map(function (torrent) {
+ return function (cb) {
+ self.remove(torrent.infoHash, cb)
+ }
+ })
+
+ if (self.dht) tasks.push(function (cb) {
+ self.dht.destroy(cb)
+ })
+
+ if (self.server) tasks.push(function (cb) {
+ try {
+ self.server.close(cb)
+ } catch (err) {
+ // ignore error, server was already closed or not listening
+ cb(null)
+ }
+ })
parallel(tasks, cb)
- return self
}
+// TODO: this probably belongs in cmd.js
WebTorrent.prototype._onTorrent = function (torrent) {
var self = this
debug('on torrent')