File: make_torrent.js
/**
* Make a torrent file that contains a thing.
*
* This program follows up on the
* <a href="http://my.brainshark.com/Distributed-Thing-Tracker-767853647">Distributed Thing Tracker</a>
* proposal to use BitTorrent as a means to distribute things.
*
* The process is quick and dirty, i.e. easy for me to code so you need to do all the work.
* In brief:
* <ul>
* <li>Create a thing.</li>
* <li>Create a torrent file with the thing files.</li>
* <li>Merge them with this program.</li>
* <li>Seed your torrent.</li>
* <li>Publish the magnet link generated by this program.</li>
* </ul>
*
* More slowly now.
*
* You need to create a design and put its metadata in a JSON format.
* Making the actual design is beyond the scope of these instructions,
* but it would be something like creating an <a href="http://www.openscad.org/">OpenSCAD</a>
* or <a href="http://openjscad.org/">OpenJSCAD</a> file.
* Once you have your design, you can create a JSON file with the
* metadata that describes it. You can start from this template, but the
* definitive source is the <a href="http://thingtracker.net/specification.html">Thing Tracker Network</a>.
*
{
"title": "Mechanical Movement #27",
"url": "http://garyhodgson.github.com/githubiverse-tst",
"author": "Gary Hodgson",
"license": "GPL3",
"tags": ["mechanical movement", "fun"],
"thumbnailURL": "https://github.com/garyhodgson/githubiverse-tst/raw/master/img/test-jig.jpg",
"description": "An implementation of movement #27 from "501 Mechanical Movements" by Henry T. Brown.\\n\\nThis is still a work in progress."
}
*
* Once you have a JSON file that has contents like the above and your design files
* you have a "thing" and can proceed to make a "distributed thing" out of it.
*
* You will need a way to create a .torrent file, which means a capable torrent client.
* I suggest <a href="http://www.utorrent.com/">µTorrent</a>.
* Do whatever is necessary with that tool to generate a .torrent file.
* I'll describe what I did.
* Push the nice friendly <a href="http://www.utorrent.com/">Get µTorrent</a> button.
* Click the <a href="http://www.utorrent.com/downloads/complete/os/win">Free Download</a>
* fine print link above the comparison chart.
* Save the uTorrent.exe file.
* Run the uTorrent.exe file by double clicking on it. This installs the µTorrent client
* (for me it was Version 3.3 Build 29342). I installed it in directory
* C:\Program Files (x86)\uTorrent and unchecked every option it offered except
* "Add an exception for µTorrent in Windows Firewall" but you can choose other options.
* The program starts up, or start it up by clicking on the <em>installed</em>
* uTorrent.exe (this one: C:\Program Files (x86)\uTorrent\uTorrent.exe).
* Create a new torrent. From the File menu, choose Create New Torrent..., or hot-key CTRL-N.
* Click the Add File button and select the design file (you could also add a directory of
* files if there were more than one).
* Remove all the tracker info from the Trackers text box.
* Add a comment in the Comment field if you want.
* Click the Create and save as... button and save the new .torrent file.
* Click Yes to the dialog box that is displayed with the text:
* "Please enter a valid tracker URL. Do you want to continue without having any trackers?"
*
* You can check the contents of the .torrent file by uploading it to
* <a href="http://www.tools4noobs.com/online_tools/torrent_decode/">Tools4noobs Torrent decode</a>.
*
* The two pieces - the .json and the .torrent can now be merged by running this program.
* This program requires <a href="http://nodejs.org/">node.js</a>, so you need to install it first.
* After that, you should have commands "node" and "npm" (the node package manager) available.
*
* The program relies on two packages, namely
* <a href="https://github.com/themasch/node-bencode">bencode</a>
* and
* <a href="https://github.com/pvorb/node-sha1">sha1</a>.
* You can install these locally (i.e. in the directory you're working in -
* the same one this file is in) with these commands:
*
npm install bencode
npm install sha1
*
* Now you can make a "thing". The command line is:
*
node make_torrent.js thing.json thing.torrent output.torrent
*
* where:
* <ul>
* <li>thing.json is the metadata file you created from the template</li>
* <li>thing.torrent is the original torrent file created by your BitTorrent client</li>
* <li>output.torrent is the file you want to create</li>
* </ul>
*
* Besides creating the output file, which is a modified torrent file containing
* the thing metadata, the program will also display a magnet link.
*
* To seed your torrent, you need to put the file(s) that it comprises and
* the generated torrent file in the directory configured for torrent seeding
* (usually the same as the one for downloads), and then add the torrent
* as an initial seed in your client. For the µTorrent client this found
* of the File menu, Add Torrent... or CTRL-O. where you can select the generated
* torrent file. This is somewhat confusing because it requires the directory
* to save it - but apparently doesn't mind if this is the same directory.
*
* Then you can send the magnet link to your geeky friends who can paste it into their
* BitTorrent client and torrent your thing. For the µTorrent client, that latter
* step is found on the File menu, Add Torrent from URL... or CTRL-U, where you can paste
* the magnet link and begin the torrent process.
*
* @module make_torrent
**/
var bencode = require ('bencode');
var fs = require ('fs');
var sha1 = require ('sha1');
// need a byte array from a buffer
// why isn't this already defined?
Buffer.prototype.toByteArray = function ()
{
return (Array.prototype.slice.call (this, 0));
}
// get the name of the JSON "thing" file
var thing = "thing.json";
if (null != process.argv[2])
thing = process.argv[2];
else
console.log ("Usage: node make_torrent.js thing.json thing.torrent output.torrent");
//get the name of the existing torrent file
var filename = "thing.torrent";
if (null != process.argv[3])
filename = process.argv[3];
//get the name of the output torrent file
var output = "output.torrent";
if (null != process.argv[4])
output = process.argv[4];
console.log ("Processing \"" + thing + "\" and \"" + filename + "\" to make \"" + output + "\"");
if (!fs.existsSync (thing))
console.log (thing + " doesn't exist");
else if (!fs.existsSync (filename))
console.log (filename + " doesn't exist");
else
{
var data = JSON.parse (fs.readFileSync (thing));
//console.log (JSON.stringify (data, null, 4));
var title = data['title'];
if (null == title)
{
var n = thing.lastIndexOf (".");
if (-1 == n)
title = thing;
else
title = thing.substring (0, n);
}
title = title.replace (/\&/g,"and");
title = title.replace (/ /g, "%20");
var file = fs.readFileSync (filename);
var torrent = bencode.decode (file);
//console.log (JSON.stringify (torrent, null, 4).substr (0, 1000));
// add our thing data
torrent['info']['thing'] = data;
fs.writeFile (output, bencode.encode (torrent));
// generate the 160 bit (40 hex characters) info primary key
var info = torrent['info'];
var primary_key = sha1 (bencode.encode (info).toByteArray ());
// spew it out as a magnet link URI
console.log ("magnet:?xt=urn:btih:" + primary_key + "&dn=" + title);
}