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-02-03 05:25:51 +0400
committerFeross Aboukhadijeh <feross@feross.org>2014-02-03 05:25:51 +0400
commit2104b76c2cc0f2e8ffb9cab1eb3fd451747e7503 (patch)
treee7a754456162b4d5cdfeacf6c5b8192c3330a6db
parente257965a02c7bdb6e94d4294d396859f9057113c (diff)
refactor
-rw-r--r--index.js207
-rw-r--r--lib/Torrent.js179
-rw-r--r--lib/TorrentManager.js63
-rw-r--r--package.json5
4 files changed, 251 insertions, 203 deletions
diff --git a/index.js b/index.js
index d9d14df..5e39bf9 100644
--- a/index.js
+++ b/index.js
@@ -22,212 +22,19 @@
// _error.apply(null, args)
// }
-var $ = require('jquery')
-var async = require('async')
-var bncode = require('bncode')
-var DHT = require('bittorrent-dht')
-var hat = require('hat')
-var magnet = require('magnet-uri')
-var portfinder = require('chrome-portfinder')
-var Swarm = require('bittorrent-swarm')
-
-var MAX_PEERS = 200
-var WIRE_TIMEOUT = 10000
-var METADATA_BLOCK_SIZE = 16 * 1024
+var TorrentManager = require('./lib/TorrentManager')
var isChromeApp = !!(typeof window !== 'undefined' && window.chrome &&
window.chrome.app && window.chrome.app.runtime)
if (isChromeApp)
console.log('This is a chrome app.')
-var peerId = '-WW0001-' + hat(48)
-
-function magnetToInfoHash (uri) {
- try {
- return magnet(uri).xt.split('urn:btih:')[1]
- } catch (e) {
- return null
- }
-}
-
-function magnetToDisplayName (uri) {
- try {
- return magnet(uri).dn
- } catch (e) {
- return null
- }
-}
-
-var magnetUri = 'magnet:?xt=urn:btih:d2474e86c95b19b8bcfdb92bc12c9d44667cfa36&dn=Leaves+of+Grass+by+Walt+Whitman.epub'
-var infoHash = magnetToInfoHash(magnetUri)
-var displayName = magnetToDisplayName(magnetUri)
-
-$('.infoHash span').text(infoHash)
-$('.displayName span').text(displayName)
-
-var dht
-var swarm
-
-async.auto({
- dhtPort: function (cb) {
- portfinder.getPort(cb)
- },
- swarmPort: function (cb) {
- portfinder.getPort(cb)
- },
- dht: ['dhtPort', function (cb, r) {
- dht = new DHT(infoHash)
-
- dht.on('node', function (node, infoHash) {
- var num = Number($('.dhtNodes span').text())
- $('.dhtNodes span').text(num + 1)
- })
-
- dht.on('peer', function (peer, infoHash) {
- var num = Number($('.dhtPeers span').text())
- $('.dhtPeers span').text(num + 1)
- // console.log('peer: ' + peer)
-
- swarm.add(peer)
- })
-
- dht.findPeers(MAX_PEERS) // TODO: should the DHT be concerned with max peers?
-
- // TODO: DHT should listen
- // dht.listen(r.dhtPort)
- }],
- swarm: ['swarmPort', function (cb, r) {
- swarm = new Swarm(infoHash, peerId, { dht: true })
-
- // TODO: add swarm listen and add ourselves to the DHT
-
- swarm.on('wire', function (wire) {
- $('.connectedPeers span').text(swarm.wires.length)
-
- // Send KEEP-ALIVE (every 60s) so peers will not disconnect the wire
- wire.setKeepAlive(true)
-
- // If peer supports DHT, send PORT message to report what port our DHT node
- // is listening on
- if (wire.peerExtensions.dht) {
- // TODO: DHT doesn't support listening yet
- // wire.port(dht.port)
- }
-
- // When peer sends PORT, add them to the routing table
- wire.on('port', function (port) {
- console.log('PORT', port)
- // TODO: DHT doesn't have a routing table
- // dht.add(wire.remoteAddress, port)
- })
-
- // Time to wait before considering requests timed out
- wire.setTimeout(WIRE_TIMEOUT)
-
- // Support extended messages:
- // - ut_metadata (metadata fetching, trackerless torrents)
- if (wire.peerExtensions) {
- console.log('Wire ' + wire.remoteAddress + ' supports extended messages', wire.peerExtensions)
- wire.extended(0, {
- m: {
- ut_metadata: 1
- }
- // TODO - this should be set once we have metadata
- // metadata_size: xx
- })
- }
-
- wire.on('extended', function (ext, buf) {
- var dict
- console.log('Received extended message ' + ext + ' from ' + wire.remoteAddress)
-
- if (ext === 0) { // handshake
-
- try {
- console.log('decoding ' + buf.toString())
- dict = bncode.decode(buf.toString())
- console.log('got extended handshake: ' + JSON.stringify(dict))
- } catch (e) {
- console.error('Error decoding extended message: ' + e.message)
- }
-
- if (dict.m.ut_metadata && dict.metadata_size) {
- var metadataSize = dict.metadata_size
- var numPieces = Math.ceil(metadataSize / METADATA_BLOCK_SIZE)
- console.log('metadata size: ' + metadataSize)
- console.log(numPieces + ' pieces')
-
- wire.metadata = new Buffer(metadataSize)
-
- // request all pieces
- for (var piece = 0; piece < numPieces; piece++) {
- wire.extended(dict.m.ut_metadata, {
- msg_type: 0,
- piece: piece
- })
- }
- }
-
- } else if (ext === 1) { // ut_metadata
-
- // 0 - request
- // 1 - data
- // 2 - reject
-
- var str
- var dataIndex
- var data
- try {
- str = buf.toString()
- console.log('decoding ' + str)
- dataIndex = str.indexOf('ee') + 2
- var msg = str.substring(0, dataIndex)
- console.log('using ' + msg)
- dict = bncode.decode(msg)
- data = buf.slice(dataIndex)
- console.log('got metadata: ' + JSON.stringify(dict))
- console.log('got metadata data: ' + data.length + ' bytes')
- } catch (e) {
- console.error('Error decoding extended message: ' + e.message)
- }
-
- // {'msg_type': 1, 'piece': 0, 'total_size': 3425}
- if (dict.msg_type === 1) { // data
- console.log('total_size: ' + dict.total_size)
- data.copy(wire.metadata, dict.piece * METADATA_BLOCK_SIZE)
-
- var errorHandler = function (err) {
- console.error('error' + err.toString())
- }
-
- chrome.fileSystem.chooseEntry({
- type: 'saveFile',
- suggestedName: displayName
- }, function (writableFileEntry) {
- writableFileEntry.createWriter(function (writer) {
- writer.onerror = errorHandler
- writer.onwriteend = function (e) {
- console.log('write complete')
- }
- writer.write(new Blob([wire.metadata]), { type: 'text/plain' })
- }, errorHandler)
- })
- }
- }
- })
-
- })
- swarm.on('error', function (err) {
- console.error(err.message)
- })
+var manager = new TorrentManager()
- // swarm.listen(r.dhtPort, function () {
- // console.log('Swarm listening on port ' + r.dhtPort)
- // })
- }]
-}, function (err) {
- if (err) console.error(err.message)
- else console.log('Setup complete')
-})
+manager.add('magnet:?xt=urn:btih:d2474e86c95b19b8bcfdb92bc12c9d44667cfa36&dn=Leaves+of+Grass+by+Walt+Whitman.epub')
+manager.on('error', function (err) {
+ console.error(err)
+ // TODO: Show error in UI somehow
+}) \ No newline at end of file
diff --git a/lib/Torrent.js b/lib/Torrent.js
new file mode 100644
index 0000000..cb1a5f9
--- /dev/null
+++ b/lib/Torrent.js
@@ -0,0 +1,179 @@
+module.exports = Torrent
+
+var bncode = require('bncode')
+var EventEmitter = require('events').EventEmitter
+var inherits = require('inherits')
+var magnet = require('magnet-uri')
+var Swarm = require('bittorrent-swarm')
+
+var METADATA_BLOCK_SIZE = 16 * 1024
+var WIRE_TIMEOUT = 10000
+
+inherits(Torrent, EventEmitter)
+
+function Torrent (uri, opts) {
+ if (!(this instanceof Torrent)) return new Torrent(uri, opts)
+ EventEmitter.call(this)
+
+ var info = this._parseMagnetUri(uri)
+ if (!info.infoHash)
+ throw new Error('invalid torrent uri')
+
+ this.infoHash = info.infoHash
+ this.displayName = info.displayName
+
+ this.swarm = new Swarm(this.infoHash, opts.peerId, { dht: true })
+
+ // TODO: swarm pooling should be smart about picking port
+ // this.swarm.listen(function (port) {
+ // console.log('Swarm listening on port ' + port)
+ // this.emit('listening', port)
+ // }.bind(this))
+
+ this.swarm.on('wire', function (wire) {
+ $('.connectedPeers span').text(this.swarm.wires.length)
+
+ // Send KEEP-ALIVE (every 60s) so peers will not disconnect the wire
+ wire.setKeepAlive(true)
+
+ // If peer supports DHT, send PORT message to report what port our DHT node
+ // is listening on
+ if (wire.peerExtensions.dht) {
+ console.log('peer supports DHT')
+ // TODO: DHT doesn't support listening yet
+ // wire.port(dht.port)
+ }
+
+ // When peer sends PORT, add them to the routing table
+ wire.on('port', function (port) {
+ console.log('received PORT: ', port)
+ // TODO: DHT doesn't have a routing table yet
+ // dht.add(wire.remoteAddress, port)
+ })
+
+ // Timeout for wire requests to this peer
+ wire.setTimeout(WIRE_TIMEOUT)
+
+ // Support extended messages:
+ // - ut_metadata (metadata fetching, trackerless torrents)
+ if (wire.peerExtensions) {
+ console.log('Wire ' + wire.remoteAddress + ' supports extended messages', wire.peerExtensions)
+ wire.extended(0, {
+ m: {
+ ut_metadata: 1
+ }
+ // TODO - this should be set once we have metadata
+ // metadata_size: xx
+ })
+ }
+
+ wire.on('extended', function (ext, buf) {
+ var dict
+ console.log('Received extended message ' + ext + ' from ' + wire.remoteAddress)
+
+ if (ext === 0) { // handshake
+
+ try {
+ console.log('decoding ' + buf.toString())
+ dict = bncode.decode(buf.toString())
+ console.log('got extended handshake: ' + JSON.stringify(dict))
+ } catch (e) {
+ console.error('Error decoding extended message: ' + e.message)
+ }
+
+ if (dict.m.ut_metadata && dict.metadata_size) {
+ var metadataSize = dict.metadata_size
+ var numPieces = Math.ceil(metadataSize / METADATA_BLOCK_SIZE)
+ console.log('metadata size: ' + metadataSize)
+ console.log(numPieces + ' pieces')
+
+ wire.metadata = new Buffer(metadataSize)
+
+ // request all pieces
+ for (var piece = 0; piece < numPieces; piece++) {
+ wire.extended(dict.m.ut_metadata, {
+ msg_type: 0,
+ piece: piece
+ })
+ }
+ }
+
+ } else if (ext === 1) { // ut_metadata
+
+ // 0 - request
+ // 1 - data
+ // 2 - reject
+
+ var str
+ var dataIndex
+ var data
+ try {
+ str = buf.toString()
+ console.log('decoding ' + str)
+ dataIndex = str.indexOf('ee') + 2
+ var msg = str.substring(0, dataIndex)
+ console.log('using ' + msg)
+ dict = bncode.decode(msg)
+ data = buf.slice(dataIndex)
+ console.log('got metadata: ' + JSON.stringify(dict))
+ console.log('got metadata data: ' + data.length + ' bytes')
+ } catch (e) {
+ console.error('Error decoding extended message: ' + e.message)
+ }
+
+ // {'msg_type': 1, 'piece': 0, 'total_size': 3425}
+ if (dict.msg_type === 1) { // data
+ console.log('total_size: ' + dict.total_size)
+ data.copy(wire.metadata, dict.piece * METADATA_BLOCK_SIZE)
+
+ var errorHandler = function (err) {
+ console.error('error' + err.toString())
+ }
+
+ chrome.fileSystem.chooseEntry({
+ type: 'saveFile',
+ suggestedName: this.displayName + '.torrent'
+ }, function (writableFileEntry) {
+ writableFileEntry.createWriter(function (writer) {
+ writer.onerror = errorHandler
+ writer.onwriteend = function (e) {
+ console.log('write complete')
+ }
+ writer.write(new Blob([wire.metadata]), { type: 'text/plain' })
+ }, errorHandler)
+ })
+ }
+ }
+ }.bind(this))
+
+ }.bind(this))
+
+ this.swarm.on('error', function (err) {
+ console.error(err.message)
+ })
+}
+
+/**
+ * Add a peer to the swarm
+ * @param {string} addr
+ */
+Torrent.prototype.addPeer = function (addr) {
+ this.swarm.add(addr)
+}
+
+//
+// HELPER METHODS
+//
+
+/**
+ * Given a magnet URI, return infoHash and displayName
+ * @param {string} uri
+ * @return {Object}
+ */
+Torrent.prototype._parseMagnetUri = function (uri) {
+ var parsed = magnet(uri)
+ return {
+ displayName: parsed.dn,
+ infoHash: parsed.xt && parsed.xt.split('urn:btih:')[1]
+ }
+}
diff --git a/lib/TorrentManager.js b/lib/TorrentManager.js
new file mode 100644
index 0000000..bc9c4eb
--- /dev/null
+++ b/lib/TorrentManager.js
@@ -0,0 +1,63 @@
+module.exports = TorrentManager
+
+var $ = require('jquery')
+var DHT = require('bittorrent-dht')
+var EventEmitter = require('events').EventEmitter
+var hat = require('hat')
+var inherits = require('inherits')
+var Torrent = require('./Torrent')
+
+var MAX_PEERS = 200
+
+inherits(TorrentManager, EventEmitter)
+
+function TorrentManager () {
+ if (!(this instanceof TorrentManager)) return new TorrentManager()
+ EventEmitter.call(this)
+
+ // TODO: should these ids be consistent between restarts?
+ this.peerId = new Buffer('-WW0001-' + hat(48), 'utf8')
+ this.nodeId = new Buffer(hat(160), 'hex')
+
+ this.torrents = {}
+
+ this.dht = new DHT({ nodeId: this.nodeId })
+
+ this.dht.on('node', this.updateUI.bind(this))
+ this.dht.on('peer', this.updateUI.bind(this))
+
+ this.dht.on('peer', function (addr, infoHash) {
+ var torrent = this.torrents[infoHash]
+ torrent.addPeer(addr)
+ }.bind(this))
+
+ // this.dht.listen()
+
+}
+
+TorrentManager.prototype.add = function (uri) {
+ var torrent = new Torrent(uri, { peerId: this.peerId })
+ this.torrents[torrent.infoHash] = torrent
+
+ torrent.on('listening', function (port) {
+ // TODO: Add the torrent to the public DHT so peers know to find up
+ })
+
+ // TODO: DHT should support multiple infoHashes
+ this.dht.setInfoHash(torrent.infoHash)
+ this.dht.findPeers(MAX_PEERS) // TODO: should the DHT be concerned with max peers?
+
+ this.updateUI()
+}
+
+// TODO: show multiple torrents
+TorrentManager.prototype.updateUI = function () {
+ // console.log('Peer ID: ' + this.peerId.toString('utf8'))
+ // console.log('Node ID: ' + this.nodeId.toString('hex'))
+ $('.infoHash span').text(this.torrents['d2474e86c95b19b8bcfdb92bc12c9d44667cfa36'].infoHash)
+ $('.displayName span').text(this.torrents['d2474e86c95b19b8bcfdb92bc12c9d44667cfa36'].displayName)
+
+ $('.dhtNodes span').text(Object.keys(this.dht.nodes).length)
+ $('.dhtPeers span').text(Object.keys(this.dht.peers).length)
+}
+
diff --git a/package.json b/package.json
index 0fe2472..88770e8 100644
--- a/package.json
+++ b/package.json
@@ -22,17 +22,16 @@
"url": "https://github.com/feross/webtorrent/issues"
},
"dependencies": {
- "async": "~0.2.10",
"bittorrent-dht": "0.x",
"bittorrent-protocol": "0.x",
"bittorrent-swarm": "0.x",
"bncode": "~0.5.2",
"browserify": "3.x",
"browserify-shim": "~3.2.2",
- "chrome-portfinder": "~0.2.2",
"hat": "0.0.3",
+ "inherits": "~2.0.1",
"jquery": "~2.1.0",
- "magnet-uri": "0.x",
+ "magnet-uri": "1.x",
"portfinder": "~0.2.1",
"read-torrent": "~0.2.0",
"speedometer": "~0.1.2"