module.exports = FileStream var debug = require('debug')('webtorrent:file-stream') var inherits = require('inherits') var stream = require('readable-stream') inherits(FileStream, stream.Readable) /** * Readable stream of a torrent file * * @param {File} file * @param {Object} opts * @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) */ function FileStream (file, opts) { stream.Readable.call(this, opts) this.destroyed = false this._torrent = file._torrent var start = (opts && opts.start) || 0 var end = (opts && opts.end && opts.end < file.length) ? opts.end : file.length - 1 var pieceLength = file._torrent.pieceLength this._startPiece = (start + file.offset) / pieceLength | 0 this._endPiece = (end + file.offset) / pieceLength | 0 this._piece = this._startPiece this._offset = (start + file.offset) - (this._startPiece * pieceLength) this._missing = end - start + 1 this._reading = false this._notifying = false this._criticalLength = Math.min((1024 * 1024 / pieceLength) | 0, 2) } FileStream.prototype._read = function () { if (this._reading) return this._reading = true this._notify() } FileStream.prototype._notify = function () { var self = this if (!self._reading || self._missing === 0) return if (!self._torrent.bitfield.get(self._piece)) { return self._torrent.critical(self._piece, self._piece + self._criticalLength) } if (self._notifying) return self._notifying = true if (self._torrent.destroyed) return self._destroy(new Error('Torrent removed')) var p = self._piece self._torrent.store.get(p, function (err, buffer) { self._notifying = false if (self.destroyed) return if (err) return self._destroy(err) debug('read %s (length %s) (err %s)', p, buffer.length, err && err.message) 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) }) self._piece += 1 } FileStream.prototype.destroy = function (onclose) { this._destroy(null, onclose) } FileStream.prototype._destroy = function (err, onclose) { if (this.destroyed) return this.destroyed = true if (!this._torrent.destroyed) { this._torrent.deselect(this._startPiece, this._endPiece, true) } if (err) this.emit('error', err) this.emit('close') if (onclose) onclose() }