create-torrent
Advanced tools
Comparing version 1.2.0 to 2.0.0
210
index.js
@@ -0,9 +1,13 @@ | ||
module.exports = createTorrent | ||
var bencode = require('bencode') | ||
var BlockStream = require('block-stream') | ||
var bencode = require('bencode') | ||
var calcPieceLength = require('piece-length') | ||
var corePath = require('path') | ||
var crypto = require('crypto') | ||
var FileReadStream = require('filestream/read') | ||
var flatten = require('lodash.flatten') | ||
var fs = require('fs') | ||
var inherits = require('inherits') | ||
var MultiStream = require('multistream') | ||
var once = require('once') | ||
@@ -13,51 +17,71 @@ var parallel = require('run-parallel') | ||
inherits(MultiFileStream, stream.Readable) | ||
var DEFAULT_ANNOUNCE_LIST = [ | ||
['udp://tracker.publicbt.com:80/announce'], | ||
['udp://tracker.openbittorrent.com:80/announce'] | ||
] | ||
function MultiFileStream (paths, opts) { | ||
stream.Readable.call(this, opts) | ||
/** | ||
* Create a torrent. | ||
* @param {string|File|FileList|Array.<File>} input | ||
* @param {Object} opts | ||
* @param {string=} opts.name | ||
* @param {string=} opts.comment | ||
* @param {string=} opts.createdBy | ||
* @param {boolean|number=} opts.private | ||
* @param {number=} opts.pieceLength | ||
* @param {Array.<Array.<string>>=} opts.announceList | ||
* @param {function} cb | ||
* @return {Buffer} buffer of .torrent file data | ||
*/ | ||
function createTorrent (input, opts, cb) { | ||
if (typeof opts === 'function') { | ||
cb = opts | ||
opts = {} | ||
} | ||
var files | ||
this._paths = paths | ||
this._currentPath = -1 | ||
if (paths.length === 0) { | ||
this._stream = null | ||
this.push(null) | ||
} else { | ||
this._nextStream() | ||
if (isFile(input)) { | ||
input = [ input ] | ||
} | ||
} | ||
MultiFileStream.prototype._read = function () {} | ||
if (Array.isArray(input) && input.length > 0) { | ||
opts.name = opts.name || input[0].name | ||
files = input.map(function (item) { | ||
if (isFile(item)) { | ||
return { | ||
length: item.size, | ||
path: [ item.name ], | ||
stream: new FileReadStream(item) | ||
} | ||
} else { | ||
// TODO: support an array of paths | ||
throw new Error('Array must contain only File objects') | ||
} | ||
}) | ||
onFiles(files, opts, cb) | ||
} else if (typeof input === 'string') { | ||
opts.name = opts.name || corePath.basename(input) | ||
MultiFileStream.prototype._nextStream = function () { | ||
this._currentPath += 1 | ||
traversePath(getFileInfo, input, function (err, files) { | ||
if (err) return cb(err) | ||
if (this._currentPath === this._paths.length) { | ||
this._stream = null | ||
this.push(null) | ||
return | ||
} | ||
if (Array.isArray(files)) { | ||
files = flatten(files) | ||
} else { | ||
files = [ files ] | ||
} | ||
this._stream = fs.createReadStream(this._paths[this._currentPath]) | ||
var dirName = corePath.normalize(input) + corePath.sep | ||
files.forEach(function (file) { | ||
file.stream = fs.createReadStream(file.path) | ||
file.path = file.path.replace(dirName, '').split(corePath.sep) | ||
}) | ||
var self = this | ||
this._stream.on('readable', function () { | ||
while (chunk = self._stream.read()) { | ||
self.push(chunk) | ||
} | ||
}) | ||
this._stream.on('end', function () { | ||
self._stream.removeAllListeners() | ||
self._nextStream() | ||
}) | ||
this._stream.on('error', function (err) { | ||
self.emit('error', err) | ||
}) | ||
onFiles(files, opts, cb) | ||
}) | ||
} else { | ||
throw new Error('invalid input type') | ||
} | ||
} | ||
var DEFAULT_ANNOUNCE_LIST = [ | ||
['udp://tracker.publicbt.com:80/announce'], | ||
['udp://tracker.openbittorrent.com:80/announce'] | ||
] | ||
function each (arr, fn, cb) { | ||
@@ -105,13 +129,16 @@ var tasks = arr.map(function (item) { | ||
var paths = files.map(function (file) { return file.path }) | ||
var streams = files.map(function (file) { | ||
return file.stream | ||
}) | ||
;(new MultiFileStream(paths)) | ||
new MultiStream(streams) | ||
.pipe(new BlockStream(pieceLength, { nopad: true })) | ||
.on('data', function (chunk) { | ||
pieces.push(crypto.createHash('sha1').update(chunk).digest()) | ||
pieces.push(sha1(chunk)) | ||
}) | ||
.on('end', function () { | ||
cb(null, pieces) | ||
cb(null, Buffer.concat(pieces)) | ||
}) | ||
.on('error', function (err) { | ||
console.error(err) | ||
cb(err) | ||
@@ -121,21 +148,4 @@ }) | ||
/** | ||
* Create a torrent. | ||
* @param {string} path | ||
* @param {Object} opts | ||
* @param {string=} opts.comment | ||
* @param {string=} opts.createdBy | ||
* @param {boolean|number=} opts.private | ||
* @param {number=} opts.pieceLength | ||
* @param {Array.<Array.<string>>=} opts.announceList | ||
* @param {function} cb | ||
* @return {Buffer} buffer of .torrent file data | ||
*/ | ||
module.exports = function (path, opts, cb) { | ||
if (typeof opts === 'function') { | ||
cb = opts | ||
opts = {} | ||
} | ||
var announceList = (opts.announceList !== undefined) | ||
function onFiles (files, opts, cb) { | ||
var announceList = opts.announceList !== undefined | ||
? opts.announceList | ||
@@ -146,14 +156,12 @@ : DEFAULT_ANNOUNCE_LIST | ||
info: { | ||
name: corePath.basename(path) | ||
name: opts.name | ||
}, | ||
announce: announceList[0][0], | ||
'announce-list': announceList, | ||
'creation date': Date.now(), | ||
'creation date': Number(opts.creationDate) || Date.now(), | ||
encoding: 'UTF-8' | ||
} | ||
var dirName = corePath.normalize(path) + corePath.sep | ||
if (opts.comment !== undefined) { | ||
torrent.info.comment = comment | ||
torrent.info.comment = opts.comment | ||
} | ||
@@ -169,40 +177,54 @@ | ||
traversePath(getFileInfo, path, function (err, files) { | ||
if (err) return cb(err) | ||
var singleFile = files.length === 1 | ||
var singleFile = !Array.isArray(files) | ||
var length = files.reduce(sumLength, 0) | ||
var pieceLength = opts.pieceLength || calcPieceLength(length) | ||
torrent.info['piece length'] = pieceLength | ||
if (singleFile) { | ||
files = [ files ] | ||
} | ||
if (singleFile) { | ||
torrent.info.length = length | ||
} | ||
files = flatten(files) | ||
getPieceList(files, pieceLength, function (err, pieces) { | ||
if (err) return cb(err) | ||
torrent.info.pieces = pieces | ||
var length = files.reduce(sumLength, 0) | ||
files.forEach(function (file) { | ||
delete file.stream | ||
}) | ||
if (singleFile) { | ||
torrent.info.length = length | ||
if (!singleFile) { | ||
torrent.info.files = files | ||
} | ||
var pieceLength = opts.pieceLength || calcPieceLength(length) | ||
torrent.info['piece length'] = pieceLength | ||
getPieceList(files, pieceLength, function (err, pieces) { | ||
torrent.info.pieces = Buffer.concat(pieces) | ||
if (!singleFile) { | ||
files.forEach(function (file) { | ||
file.path = file.path.replace(dirName, '').split(corePath.sep) | ||
}) | ||
torrent.info.files = files | ||
} | ||
cb(null, bencode.encode(torrent)) | ||
}) | ||
cb(null, bencode.encode(torrent)) | ||
}) | ||
} | ||
/** | ||
* Accumulator to sum file lengths | ||
* @param {number} sum | ||
* @param {Object} file | ||
* @return {number} | ||
*/ | ||
function sumLength (sum, file) { | ||
return sum + file.length | ||
} | ||
/** | ||
* Check if `obj` is a W3C File object | ||
* @param {*} obj | ||
* @return {boolean} | ||
*/ | ||
function isFile (obj) { | ||
return typeof File !== 'undefined' && obj instanceof File | ||
} | ||
/** | ||
* Compute a SHA1 hash | ||
* @param {Buffer} buf | ||
* @return {Buffer} | ||
*/ | ||
function sha1 (buf) { | ||
return crypto.createHash('sha1').update(buf).digest() | ||
} |
{ | ||
"name": "create-torrent", | ||
"description": "Create .torrent files", | ||
"version": "1.2.0", | ||
"version": "2.0.0", | ||
"author": "Feross Aboukhadijeh <feross@feross.org> (http://feross.org/)", | ||
"bin": { | ||
"create-torrent": "./bin/cmd.js" | ||
}, | ||
"browser": { | ||
"minimist": false | ||
}, | ||
"bugs": { | ||
@@ -12,4 +18,7 @@ "url": "https://github.com/feross/create-torrent/issues" | ||
"block-stream": "^0.0.7", | ||
"filestream": "git://github.com/feross/filestream", | ||
"inherits": "^2.0.1", | ||
"lodash.flatten": "^2.4.1", | ||
"minimist": "^0.2.0", | ||
"multistream": "^1.0.0", | ||
"once": "^1.3.0", | ||
@@ -20,2 +29,3 @@ "piece-length": "^0.0.0", | ||
"devDependencies": { | ||
"brfs": "^1.1.2", | ||
"parse-torrent": "^1.1.0", | ||
@@ -46,3 +56,19 @@ "tape": "^2.5.0" | ||
"test": "tape test/*.js" | ||
}, | ||
"testling": { | ||
"files": "test/browser/*.js", | ||
"browsers": [ | ||
"ie/9..latest", | ||
"chrome/25..latest", | ||
"chrome/canary", | ||
"firefox/20..latest", | ||
"firefox/nightly", | ||
"safari/6..latest", | ||
"opera/15.0..latest", | ||
"opera/next", | ||
"ipad/6.0..latest", | ||
"iphone/6.0..latest", | ||
"android-browser/4.2..latest" | ||
] | ||
} | ||
} |
@@ -5,5 +5,7 @@ # create-torrent [![travis](https://img.shields.io/travis/feross/create-torrent.svg)](https://travis-ci.org/feross/create-torrent) [![npm](https://img.shields.io/npm/v/create-torrent.svg)](https://npmjs.org/package/create-torrent) [![gittip](https://img.shields.io/gittip/feross.svg)](https://www.gittip.com/feross/) | ||
[![browser support](https://ci.testling.com/feross/create-torrent.png)](https://ci.testling.com/feross/create-torrent) | ||
![creation](https://raw.githubusercontent.com/feross/create-torrent/master/img.jpg) | ||
This module is used by [WebTorrent](http://webtorrent.io). | ||
This module is used by [WebTorrent](http://webtorrent.io). Works in the browser with [browserify](http://browserify.org/)! | ||
@@ -37,17 +39,24 @@ ### install | ||
#### `createTorrent(path, [opts], function callback (err, torrent) {})` | ||
#### `createTorrent(input, [opts], function callback (err, torrent) {})` | ||
Create a new `.torrent` file. | ||
`path` is the path to the file or folder to use. | ||
`input` can be any of the following: | ||
`opts` is optional and allows you to set special settings for the .torrent. | ||
- path to the file or folder on filesystem (string) | ||
- W3C [File](https://developer.mozilla.org/en-US/docs/Web/API/File) object (from an <input> or drag and drop) | ||
- W3C [FileList](https://developer.mozilla.org/en-US/docs/Web/API/FileList) object (basically an array of `File` objects) | ||
- Array of `File` objects | ||
`opts` is optional and allows you to set special settings on the produced .torrent file. | ||
``` js | ||
{ | ||
comment: '', // free-form textual comments of the author (string) | ||
createdBy: '', // name and version of the program used to create the .torrent (string) | ||
private: false, // is this a private .torrent? (boolean or integer) | ||
pieceLength: 32768 // force a custom piece length (number of bytes) | ||
announceList: [[]] // custom trackers to use (array of arrays of strings) (see [bep12](http://www.bittorrent.org/beps/bep_0012.html)) | ||
name: String, // name of the torrent (default = basename of `path`) | ||
comment: String, // free-form textual comments of the author | ||
createdBy: String, // name and version of program used to create torrent | ||
creationDate: Date // creation time in UNIX epoch format (default = now) | ||
private: Boolean, // is this a private .torrent? (default = false) | ||
pieceLength: Number // force a custom piece length (number of bytes) | ||
announceList: [[String]] // custom trackers (array of arrays of strings) (see [bep12](http://www.bittorrent.org/beps/bep_0012.html)) | ||
} | ||
@@ -59,4 +68,15 @@ ``` | ||
### command line | ||
``` | ||
usage: create-torrent <directory OR file> {-o outfile.torrent} | ||
Create a torrent file from a directory or file. | ||
If an output file isn\'t specified with `-o`, the torrent file will be | ||
written to stdout. | ||
``` | ||
### license | ||
MIT. Copyright (c) [Feross Aboukhadijeh](http://feross.org). |
@@ -70,3 +70,2 @@ var createTorrent = require('../') | ||
test('create multi file torrent', function (t) { | ||
@@ -118,3 +117,2 @@ t.plan(16) | ||
]) | ||
t.equals(sha1(parsedTorrent.infoBuffer), '80562f38656b385ea78959010e51a2cc9db41ea0') | ||
@@ -121,0 +119,0 @@ }) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Git dependency
Supply chain riskContains a dependency which resolves to a remote git URL. Dependencies fetched from git URLs are not immutable can be used to inject untrusted code or reduce the likelihood of a reproducible install.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
421936
20
468
80
10
3
1
3
+ Addedminimist@^0.2.0
+ Addedmultistream@^1.0.0
+ Addedminimist@0.2.4(transitive)
+ Addedmultistream@1.6.1(transitive)