Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support seeding a file from filesystem (path string) #209

Merged
merged 6 commits into from Dec 17, 2014
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

@@ -1,6 +1,6 @@
ui: tape
browsers:
- name: chrome
version: 35..latest
version: 39..latest
- name: firefox
version: 30..latest
version: 34..latest
@@ -1,14 +1,17 @@
# ![WebTorrent](img/wordmark.png)

### Streaming torrent client for node & the browser

[![Build Status][webtorrent-ti]][webtorrent-tu]
[![NPM Version][webtorrent-ni]][webtorrent-nu]
[![NPM Downloads][webtorrent-downloads-image]][webtorrent-downloads-url]
[![Gratipay][webtorrent-gratipay-image]][webtorrent-gratipay-url]

### WebTorrent – Streaming torrent client for node & the browser
[![Sauce Test Status][webtorrent-sauce-image]][webtorrent-sauce-url]

WebTorrent is a streaming torrent client that works in node.js and the browser. **YEP,
THAT'S RIGHT. The browser.** It's written completely in JavaScript – the language of the
web – so the same code works in both runtimes.
**WebTorrent** is a streaming torrent client for **node.js** and the **browser**. YEP,
THAT'S RIGHT. THE BROWSER. It's written completely in JavaScript – the language of the web
– so the same code works in both runtimes.

In the browser, WebTorrent uses **WebRTC** (data channels) for peer-to-peer transport.
It can be used **without** browser plugins, extensions, or installations. It's Just
@@ -33,8 +36,6 @@ it the first "hybrid" client.

> Warning: This is pre-alpha software. **Watch/star to follow along with progress.**
[![Sauce Test Status][webtorrent-sauce-image]][webtorrent-sauce-url]

### Features

- **Torrent client for node.js & the browser** (same npm module!)
@@ -9,15 +9,17 @@ var DHT = require('bittorrent-dht/client') // browser exclude
var EventEmitter = require('events').EventEmitter
var extend = require('extend.js')
var FileReadStream = require('filestream/read')
var FSStorage = require('./lib/fs-storage') // browser exclude
var fs = require('fs')
var hat = require('hat')
var inherits = require('inherits')
var loadIPSet = require('load-ip-set') // browser exclude
var parallel = require('run-parallel')
var parseTorrent = require('parse-torrent')
var speedometer = require('speedometer')
var Storage = require('./lib/storage')
var stream = require('stream')

var FSStorage = require('./lib/fs-storage') // browser exclude
var Storage = require('./lib/storage')
var Torrent = require('./lib/torrent')

inherits(WebTorrent, EventEmitter)
@@ -34,7 +36,7 @@ function WebTorrent (opts) {
if (!debug.enabled) self.setMaxListeners(0)

self.torrentPort = opts.torrentPort || 0
self.tracker = (opts.tracker !== undefined) ? opts.tracker : true
self.tracker = opts.tracker !== undefined ? opts.tracker : true
self.torrents = []

self.downloadSpeed = speedometer()
@@ -196,45 +198,49 @@ WebTorrent.prototype.seed = function (input, opts, onseed) {
}
if (!opts) opts = {}

// TODO: support `input` as string, or array of strings
// TODO: support an array of paths
// TODO: support path to folder (currently, only path to file supported)

if (typeof FileList !== 'undefined' && input instanceof FileList)
input = Array.prototype.slice.call(input)

if (isBlob(input) || Buffer.isBuffer(input)) {
if (isBlob(input) || Buffer.isBuffer(input))
input = [ input ]
}

var streams = input.map(function (item) {
if (isBlob(item)) return new FileReadStream(item)
else if (Buffer.isBuffer(item)) {
var s = new stream.PassThrough()
s.end(item)
return s
} else throw new Error('unsupported input type to `seed`')
})
var streams
if (Array.isArray(input) && input.length > 0) {
streams = input.map(function (item) {
if (isBlob(item)) return new FileReadStream(item)
else if (Buffer.isBuffer(item)) {
var s = new stream.PassThrough()
s.end(item)
return s
} else {
throw new Error('Array must contain only File|Blob|Buffer objects')
}
})
} else if (typeof input === 'string') {
streams = [ fs.createReadStream(input) ]
} else {
throw new Error('invalid input type')
}

var torrent
createTorrent(input, opts, function (err, torrentBuf) {
if (err) return self.emit('error', err)
self.add(torrentBuf, opts, function (_torrent) {
torrent = _torrent
torrent.storage.load(
streams,
function (err) {
if (err) return self.emit('error', err)
self.emit('seed', torrent)
})
self.add(torrentBuf, opts, function (torrent) {
var tasks = [function (cb) {
torrent.storage.load(streams, cb)
}]
if (self.dht) tasks.push(function (cb) {
torrent.on('dhtAnnounce', cb)
})
parallel(tasks, function (err) {
if (err) return self.emit('error', err)
if (onseed) onseed(torrent)
self.emit('seed', torrent)
})
})
})

function clientOnSeed (_torrent) {
if (torrent.infoHash === _torrent.infoHash) {
onseed(torrent)
self.removeListener('seed', clientOnSeed)
}
}
if (onseed) self.on('seed', clientOnSeed)
}

/**
@@ -165,6 +165,7 @@ FSStorage.prototype._onPieceDone = function (piece) {

var target = targets[i++]
target.openWrite(function (err, file) {
if (self.closed) return
if (err) return self.emit('error', err)
file.write(target.offset, piece.buffer.slice(target.from, target.to), writeToNextFile)
})
@@ -8,11 +8,7 @@ var test = require('tape')
var leavesPath = __dirname + '/torrents/leaves.torrent'
var leaves = fs.readFileSync(leavesPath)
var leavesTorrent = parseTorrent(leaves)

function verify (t, client, torrent) {
t.equal(torrent.infoHash, leavesTorrent.infoHash)
client.destroy()
}
var leavesBookPath = __dirname + '/content/Leaves of Grass by Walt Whitman.epub'

test('client.add (http url to a torrent file (string))', function (t) {
t.plan(1)
@@ -25,9 +21,10 @@ test('client.add (http url to a torrent file (string))', function (t) {
if (err) throw err
server.listen(port, function () {
var url = 'http://127.0.0.1:' + port
var client1 = new WebTorrent({ dht: false, trackers: false })
client1.add(url, function (torrent) {
verify(t, client1, torrent)
var client = new WebTorrent({ dht: false, tracker: false })
client.add(url, function (torrent) {
t.equal(torrent.infoHash, leavesTorrent.infoHash)
client.destroy()
server.close()
})
})
@@ -37,8 +34,19 @@ test('client.add (http url to a torrent file (string))', function (t) {
test('client.add (filesystem path to a torrent file (string))', function (t) {
t.plan(1)

var client1 = new WebTorrent({ dht: false, trackers: false })
client1.add(leavesPath, function (torrent) {
verify(t, client1, torrent)
var client = new WebTorrent({ dht: false, tracker: false })
client.add(leavesPath, function (torrent) {
t.equal(torrent.infoHash, leavesTorrent.infoHash)
client.destroy()
})
})

test('client.seed (filesystem path to file (string))', function (t) {
t.plan(1)

var client = new WebTorrent({ dht: false, tracker: false })
client.seed(leavesBookPath, function (torrent) {
t.equal(torrent.infoHash, leavesTorrent.infoHash)
client.destroy()
})
})
@@ -5,6 +5,7 @@ var test = require('tape')

var leaves = fs.readFileSync(__dirname + '/torrents/leaves.torrent')
var leavesTorrent = parseTorrent(leaves)
var leavesBook = fs.readFileSync(__dirname + '/content/Leaves of Grass by Walt Whitman.epub')

function verify (t, client, torrent) {
t.equal(torrent.infoHash, leavesTorrent.infoHash)
@@ -15,23 +16,52 @@ test('client.add (magnet uri, torrent file, info hash, and parsed torrent)', fun
t.plan(5)

// magnet uri (utf8 string)
var client1 = new WebTorrent({ dht: false, trackers: false })
var client1 = new WebTorrent({ dht: false, tracker: false })
verify(t, client1, client1.add('magnet:?xt=urn:btih:' + leavesTorrent.infoHash))

// torrent file (buffer)
var client2 = new WebTorrent({ dht: false, trackers: false })
var client2 = new WebTorrent({ dht: false, tracker: false })
verify(t, client2, client2.add(leaves))

// info hash (hex string)
var client3 = new WebTorrent({ dht: false, trackers: false })
var client3 = new WebTorrent({ dht: false, tracker: false })
verify(t, client3, client3.add(leavesTorrent.infoHash))

// info hash (buffer)
var client4 = new WebTorrent({ dht: false, trackers: false })
var client4 = new WebTorrent({ dht: false, tracker: false })
verify(t, client4, client4.add(new Buffer(leavesTorrent.infoHash, 'hex')))

// parsed torrent (from parse-torrent)
var client5 = new WebTorrent({ dht: false, trackers: false })
var client5 = new WebTorrent({ dht: false, tracker: false })
verify(t, client5, client5.add(leavesTorrent))
})

test('client.seed (Buffer, Blob)', function (t) {
t.plan(2)

var opts = {
name: 'Leaves of Grass by Walt Whitman.epub'
}

// torrent file (Buffer)
var client1 = new WebTorrent({ dht: false, tracker: false })
client1.seed(leavesBook, opts, function (torrent) {
verify(t, client1, torrent)
})

// Blob
if (typeof Blob !== 'undefined') {
var client2 = new WebTorrent({ dht: false, tracker: false })
var blob = new Blob([ leavesBook ])

// TODO: just pass name in the opts object – this should work
// Doing it this way until we use the create-torrent code to process inputs
// in client.seed
blob.name = opts.name
client2.seed(blob, function (torrent) {
verify(t, client2, torrent)
})
} else {
t.pass('Skipping Blob test because missing `Blob` constructor')
}
})
@@ -1,13 +1,12 @@
var auto = require('run-auto')
var WebTorrent = require('../')
var BlockStream = require('block-stream')
var DHT = require('bittorrent-dht/client')
var fs = require('fs')
var parseTorrent = require('parse-torrent')
var test = require('tape')
var TrackerServer = require('bittorrent-tracker/server')

var leavesFile = __dirname + '/torrents/Leaves of Grass by Walt Whitman.epub'
var leavesFile = __dirname + '/content/Leaves of Grass by Walt Whitman.epub'
var leavesTorrent = fs.readFileSync(__dirname + '/torrents/leaves.torrent')
var leavesParsed = parseTorrent(leavesTorrent)

@@ -212,7 +211,7 @@ test('Simple download using DHT', function (t) {
},
client1: ['dhtPort', function (cb, r) {
var client1 = new WebTorrent({
trackers: false,
tracker: false,
dht: { bootstrap: '127.0.0.1:' + r.dhtPort }
})
client1.on('error', function (err) { t.fail(err) })
@@ -245,7 +244,7 @@ test('Simple download using DHT', function (t) {

client2: ['client1', function (cb, r) {
var client2 = new WebTorrent({
trackers: false,
tracker: false,
dht: { bootstrap: '127.0.0.1:' + r.dhtPort }
})
client2.on('error', function (err) { t.fail(err) })
@@ -9,8 +9,8 @@ var leavesTorrent = parseTorrent(leaves)
test('ut_metadata transfer', function (t) {
t.plan(5)

var client1 = new WebTorrent({ dht: false, trackers: false })
var client2 = new WebTorrent({ dht: false, trackers: false })
var client1 = new WebTorrent({ dht: false, tracker: false })
var client2 = new WebTorrent({ dht: false, tracker: false })

client1.on('torrent', function (torrent) {
t.pass('client1 emits torrent event') // even though it started with metadata
@@ -5,7 +5,7 @@ var portfinder = require('portfinder')
var test = require('tape')
var WebTorrent = require('../')

var leavesFile = __dirname + '/torrents/Leaves of Grass by Walt Whitman.epub'
var leavesFile = __dirname + '/content/Leaves of Grass by Walt Whitman.epub'
var leavesTorrent = fs.readFileSync(__dirname + '/torrents/leaves.torrent')

test('start http server programmatically', function (t) {
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.