diff --git a/index.js b/index.js index 4d707552..261b3801 100644 --- a/index.js +++ b/index.js @@ -15,7 +15,17 @@ const stream = require('readable-stream') const getFiles = require('./get-files') // browser exclude -const announceList = [ +// TODO: When Node 10 support is dropped, replace this with Array.prototype.flat +function flat (arr1) { + return arr1.reduce( + (acc, val) => Array.isArray(val) + ? acc.concat(flat(val)) + : acc.concat(val), + [] + ) +} + +const ANNOUNCE_LIST = [ ['udp://tracker.leechers-paradise.org:6969'], ['udp://tracker.coppersurfer.tk:6969'], ['udp://tracker.opentrackr.org:1337'], @@ -274,20 +284,29 @@ function getPieceList (files, pieceLength, estimatedTorrentLength, opts, cb) { } function onFiles (files, opts, cb) { - let announceList = opts.announceList + let announce = null + let announceList = [] - if (!announceList) { - if (typeof opts.announce === 'string') announceList = [[opts.announce]] - else if (Array.isArray(opts.announce)) { - announceList = opts.announce.map(u => [u]) - } + if (opts.announce) { + let trackerList = opts.announce + if (!Array.isArray(trackerList)) trackerList = [trackerList] + + const [first, ...rest] = trackerList.filter(u => typeof u === 'string') + if (first) announce = first + announceList = announceList.concat([[first], ...rest.map(u => [u])]) } - if (!announceList) announceList = [] + if (opts.announceList && Array.isArray(opts.announceList)) { + opts.announceList.forEach(l => { + if (!Array.isArray(l)) return + const trackerList = l.filter(u => typeof u === 'string') + if (trackerList.length > 0) announceList.push(trackerList) + }) + } if (globalThis.WEBTORRENT_ANNOUNCE) { if (typeof globalThis.WEBTORRENT_ANNOUNCE === 'string') { - announceList.push([[globalThis.WEBTORRENT_ANNOUNCE]]) + announceList.push([globalThis.WEBTORRENT_ANNOUNCE]) } else if (Array.isArray(globalThis.WEBTORRENT_ANNOUNCE)) { announceList = announceList.concat(globalThis.WEBTORRENT_ANNOUNCE.map(u => [u])) } @@ -295,9 +314,11 @@ function onFiles (files, opts, cb) { // When no trackers specified, use some reasonable defaults if (opts.announce === undefined && opts.announceList === undefined) { - announceList = announceList.concat(module.exports.announceList) + announceList = announceList.concat(ANNOUNCE_LIST) } + if (!announce && announceList.length > 0) announce = announceList[0][0] + if (typeof opts.urlList === 'string') opts.urlList = [opts.urlList] const torrent = { @@ -308,10 +329,9 @@ function onFiles (files, opts, cb) { encoding: 'UTF-8' } - if (announceList.length !== 0) { - torrent.announce = announceList[0][0] - torrent['announce-list'] = announceList - } + if (announce) torrent.announce = announce + + if (announceList.length !== 0) torrent['announce-list'] = announceList if (opts.comment !== undefined) torrent.comment = opts.comment @@ -451,5 +471,5 @@ function getStreamStream (readable, file) { module.exports = createTorrent module.exports.parseInput = parseInput -module.exports.announceList = announceList +module.exports.announceList = ANNOUNCE_LIST module.exports.isJunkPath = isJunkPath diff --git a/test/announce.js b/test/announce.js new file mode 100644 index 00000000..4f34ec2a --- /dev/null +++ b/test/announce.js @@ -0,0 +1,102 @@ +const fixtures = require('webtorrent-fixtures') +const parseTorrent = require('parse-torrent') +const test = require('tape') +const createTorrent = require('../') + +test('announce is added to torrent', t => { + t.plan(4) + + createTorrent(fixtures.leaves.contentPath, { + announce: 'wss://example1.com' + }, (err, torrent) => { + t.error(err) + + const parsedTorrent = parseTorrent(torrent) + + t.ok(Array.isArray(parsedTorrent.announce)) + + t.equals(parsedTorrent.announce.length, 1) + + t.ok(parsedTorrent.announce.includes('wss://example1.com')) + }) +}) + +test('invalid announceList as string', t => { + t.plan(3) + + createTorrent(fixtures.leaves.contentPath, { + announceList: 'wss://example1.com' + }, (err, torrent) => { + t.error(err) + + const parsedTorrent = parseTorrent(torrent) + + t.ok(Array.isArray(parsedTorrent.announce)) + + t.equals(parsedTorrent.announce.length, 0) + }) +}) + +test('announceList is a list of lists of strings', t => { + t.plan(6) + + createTorrent(fixtures.leaves.contentPath, { + announceList: [['wss://example1.com', 'wss://example2.com'], ['wss://example3.com']] + }, (err, torrent) => { + t.error(err) + + const parsedTorrent = parseTorrent(torrent) + + t.ok(Array.isArray(parsedTorrent.announce)) + + t.equals(parsedTorrent.announce.length, 3) + + t.ok(parsedTorrent.announce.includes('wss://example1.com')) + + t.ok(parsedTorrent.announce.includes('wss://example2.com')) + + t.ok(parsedTorrent.announce.includes('wss://example3.com')) + }) +}) + +test('verify that announce and announceList can be used together', t => { + t.plan(5) + + createTorrent(fixtures.leaves.contentPath, { + announce: 'wss://example1.com', + announceList: [['wss://example2.com']] + }, (err, torrent) => { + t.error(err) + + const parsedTorrent = parseTorrent(torrent) + + t.ok(Array.isArray(parsedTorrent.announce)) + + t.equals(parsedTorrent.announce.length, 2) + + t.ok(parsedTorrent.announce.includes('wss://example1.com')) + + t.ok(parsedTorrent.announce.includes('wss://example2.com')) + }) +}) + +test('invalid trackers are discarded', t => { + t.plan(5) + + createTorrent(fixtures.leaves.contentPath, { + announce: 'wss://example1.com', + announceList: [['wss://example2.com'], [1234, ['wss://thisisinsidealist.com']]] + }, (err, torrent) => { + t.error(err) + + const parsedTorrent = parseTorrent(torrent) + + t.ok(Array.isArray(parsedTorrent.announce)) + + t.equals(parsedTorrent.announce.length, 2) + + t.ok(parsedTorrent.announce.includes('wss://example1.com')) + + t.ok(parsedTorrent.announce.includes('wss://example2.com')) + }) +})