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:
Diffstat (limited to 'lib/Torrent.js')
-rw-r--r--lib/Torrent.js179
1 files changed, 179 insertions, 0 deletions
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]
+ }
+}