diff options
author | Feross Aboukhadijeh <feross@feross.org> | 2014-09-21 05:41:24 +0400 |
---|---|---|
committer | Feross Aboukhadijeh <feross@feross.org> | 2014-09-21 05:41:24 +0400 |
commit | 93686505fbc90522c75b6c151ec7261aa76098de (patch) | |
tree | 5b474b920e79b7b39b6804fac5e6a641de6ae843 /lib/file-stream.js | |
parent | 2e14192c311f64c20496a72af7ffce36495be92b (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 'lib/file-stream.js')
-rw-r--r-- | lib/file-stream.js | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/lib/file-stream.js b/lib/file-stream.js new file mode 100644 index 0000000..6d86ea8 --- /dev/null +++ b/lib/file-stream.js @@ -0,0 +1,99 @@ +module.exports = FileStream + +var debug = require('debug')('webtorrent:file-stream') +var inherits = require('inherits') +var stream = require('stream') + +inherits(FileStream, stream.Readable) + +/** + * A readable stream of a torrent file. + * + * @param {Object} file + * @param {number} opts.start stream slice of file, starting from this byte (inclusive) + * @param {number} opts.end stream slice of file, ending with this byte (inclusive) + * @param {number} opts.pieceLength length of an individual piece + */ +function FileStream (file, opts) { + var self = this + if (!(self instanceof FileStream)) return new FileStream(file, opts) + stream.Readable.call(self, opts) + debug('new filestream %s', JSON.stringify(opts)) + + if (!opts) opts = {} + if (!opts.start) opts.start = 0 + if (!opts.end) opts.end = file.length - 1 + + self.length = opts.end - opts.start + 1 + + var offset = opts.start + file.offset + var pieceLength = opts.pieceLength + + self.startPiece = offset / pieceLength | 0 + self.endPiece = (opts.end + file.offset) / pieceLength | 0 + + self._storage = file.storage + self._piece = self.startPiece + self._missing = self.length + self._reading = false + self._notifying = false + self._destroyed = false + self._criticalLength = Math.min((1024 * 1024 / pieceLength) | 0, 2) + self._offset = offset - (self.startPiece * pieceLength) +} + +FileStream.prototype._read = function () { + debug('_read') + var self = this + if (self._reading) return + self._reading = true + self.notify() +} + +FileStream.prototype.notify = function () { + debug('notify') + var self = this + + if (!self._reading || self._missing === 0) return + if (!self._storage.bitfield.get(self._piece)) + return self._storage.emit('critical', self._piece, self._piece + self._criticalLength) + + if (self._notifying) return + self._notifying = true + + var p = self._piece + debug('before read %s', p) + self._storage.read(self._piece++, function (err, buffer) { + debug('after read %s (buffer.length %s) (err %s)', p, buffer.length, (err && err.message) || err) + self._notifying = false + + if (self._destroyed) return + + if (err) { + self._storage.emit('error', err) + return self.destroy(err) + } + + if (self._offset) { + buffer = buffer.slice(self._offset) + self._offset = 0 + } + + if (self._missing < buffer.length) { + buffer = buffer.slice(0, self._missing) + } + self._missing -= buffer.length + + debug('pushing buffer of length %s', buffer.length) + self._reading = false + self.push(buffer) + + if (self._missing === 0) self.push(null) + }) +} + +FileStream.prototype.destroy = function () { + var self = this + if (self._destroyed) return + self._destroyed = true +} |