UNPKG

parse-torrent

Version:

Parse a torrent and return an object of keys/values

137 lines (112 loc) 3.56 kB
module.exports = parseTorrent module.exports.toBuffer = toBuffer var bencode = require('bencode') var path = require('path') var Rusha = require('rusha-browserify') // Fast SHA1 (works in browser) /** * Parse a torrent. Throws an exception if the torrent is missing required fields. * @param {Buffer|Object} torrent * @return {Object} parsed torrent */ function parseTorrent (torrent) { if (Buffer.isBuffer(torrent)) { torrent = bencode.decode(torrent) } // sanity check ensure(torrent.info, 'info') ensure(torrent.info.name, 'info.name') ensure(torrent.info['piece length'], 'info[\'piece length\']') ensure(torrent.info.pieces, 'info.pieces') if (torrent.info.files) { torrent.info.files.forEach(function (file) { ensure(typeof file.length === 'number', 'info.files[0].length') ensure(file.path, 'info.files[0].path') }) } else { ensure(torrent.info.length, 'info.length') } var result = {} result.info = torrent.info result.infoBuffer = bencode.encode(torrent.info) result.infoHash = sha1(result.infoBuffer) result.name = torrent.info.name.toString() result.private = !!torrent.info.private if (torrent['creation date']) result.created = new Date(torrent['creation date'] * 1000) // announce/announce-list may be missing if metadata fetched via ut_metadata extension var announce = torrent['announce-list'] if (!announce) { if (torrent.announce) { announce = [[torrent.announce]] } else { announce = [] } } result.announceList = announce.map(function (urls) { return urls.map(function (url) { return url.toString() }) }) result.announce = [].concat.apply([], result.announceList) var files = torrent.info.files || [torrent.info] result.files = files.map(function (file, i) { var parts = [].concat(file.name || result.name, file.path || []).map(function (p) { return p.toString() }) return { path: path.join.apply(null, [path.sep].concat(parts)).slice(1), name: parts[parts.length - 1], length: file.length, offset: files.slice(0, i).reduce(sumLength, 0) } }) result.length = files.reduce(sumLength, 0) var lastFile = result.files[result.files.length - 1] result.pieceLength = torrent.info['piece length'] result.lastPieceLength = ((lastFile.offset + lastFile.length) % result.pieceLength) || result.pieceLength result.pieces = splitPieces(torrent.info.pieces) return result } /** * Convert a parsed torrent object back into a .torrent file buffer. * @param {Object} parsed parsed torrent * @return {Buffer} */ function toBuffer (parsed) { var torrent = { info: parsed.info } if (parsed.announceList) { torrent['announce-list'] = parsed.announceList.map(function (urls) { return urls.map(function (url) { url = new Buffer(url, 'utf8') if (!torrent.announce) { torrent.announce = url } return url }) }) } if (parsed.created) { torrent['creation date'] = (parsed.created.getTime() / 1000) | 0 } return bencode.encode(torrent) } function sumLength (sum, file) { return sum + file.length } function splitPieces (buf) { var pieces = [] for (var i = 0; i < buf.length; i += 20) { pieces.push(buf.slice(i, i + 20).toString('hex')) } return pieces } function sha1 (buf) { return (new Rusha()).digestFromBuffer(buf) } function ensure (bool, fieldName) { if (!bool) { throw new Error('Torrent is missing required field: ' + fieldName) } }