var cheerio = require("cheerio"),
ent = require("ent"),
filesize_parser = require("filesize-parser"),
request = require("request"),
url = require("url");This is where all the fun happens. Please take a moment to read through this file to acquaint yourself with the functionality provided and the implementation thereof.
var cheerio = require("cheerio"),
ent = require("ent"),
filesize_parser = require("filesize-parser"),
request = require("request"),
url = require("url");Main entry point. This is the client class. It takes a single optional argument, being the base URL of the NyaaTorrents site you want to interact with. If left out, it will default to "http://www.nyaa.eu/".
var NyaaTorrents = module.exports = function NyaaTorrents(base_url) {
if (typeof base_url === "undefined") {
base_url = "http://www.nyaa.eu/";
}
this.base_url = base_url;
};Search method. This maps pretty transparently to the search page,
passing through the query hash verbatim as url parameters. If the query
argument is left out, you'll get a list of the latest torrents as you will
have provided no filter arguments. The second argument is a callback that
will be called on completion with err and results arguments. err will
be null in the case of success.
NyaaTorrents.prototype.search = function search(query, cb) {
var uri = url.parse(this.base_url);
if (typeof query === "function") {
cb = query;
query = null;
}
if (typeof query !== "object" || query === null) {
query = {};
}
query.page = "torrents";
uri.query = query;
request(url.format(uri), function(err, res, data) {
if (err) {
return cb(err);
}
var $ = cheerio.load(data);Our results are found in a table with a predictable structure. Some of this code might be fragile - expect updates here to improve performance or stability. PATCHES WELCOME LOL.
var torrents = Array.prototype.slice.apply($("table.tlist .tlistrow")).map(function(row) {
var obj = {};If we can't find the download link or the category image, we just give up on this row. It shouldn't happen, but it might indicate bad markup or unhandled stuff.
var download_link = $(row).find(".tlistdownload a")[0];
if (!download_link) {
return null;
}
var category_image = $(row).find(".tlisticon a")[0];
if (!category_image) {
return null;
}
obj.id = parseInt(download_link.attribs.href.trim().replace(/^.+?(\d+)$/, "$1"), 10);
obj.name = $(row).find(".tlistname").text().trim();
obj.categories = ent.decode(category_image.attribs.title).trim().split(/ >> /g).map(function(e) { return e.toLowerCase().trim().replace(/\s+/g, "-"); });
obj.flags = row.attribs.class.split(/ /g).filter(function(e) { return e !== "tlistrow"; });
obj.size = filesize_parser($(row).find(".tlistsize").text().trim());
obj.seeds = parseInt($(row).find(".tlistsn").text().trim(), 10);
obj.leeches = parseInt($(row).find(".tlistln").text().trim(), 10);
obj.downloads = parseInt($(row).find(".tlistdn").text().trim(), 10);
obj.comments = parseInt($(row).find(".tlistmn").text().trim(), 10);
return obj;
}).filter(function(e) {
return e !== null;
});
return cb(null, torrents);
});
};This method gets the information about a specific torrent, identified by ID.
First argument is a number or a string containing the torrent ID, second is
a callback to be called on completion with err and result arguments. As
with the previous method, err will be null in the case of success.
NyaaTorrents.prototype.get = function get(id, cb) {
var uri = url.parse(this.base_url);
uri.query = {
page: "torrentinfo",
tid: id,
};
request(url.format(uri), function(err, res, data) {
if (err) {
return cb(err);
}
var $ = cheerio.load(data);
var content = $(".content")[0];When there's an error, it's displayed as text in the spot where the page content would usually go. We pass that through as-is to the user.
if (content.children.length === 1 && content.children[0].type === "text") {
return cb(Error(ent.decode(content.children[0].data).trim()));
}
var obj = {};These are things like "trusted" or "remake". See the NyaaTorrents manual for more information.
obj.flags = content.attribs.class.split(/ /g).filter(function(e) { return e !== "content"; });Categories. Super simple stuff. These are lower-cased, hyphen-delimited, human-readable strings.
obj.categories = Array.prototype.slice.apply($(content).find("td.viewinfocategory a")).map(function(e) {
return $(e).text().toLowerCase().trim().replace(/\s+/g, "-");
});The torrent details are displayed in a wonky table thing. Each field is displayed as a pair of cells, with the former containing the field name and the latter displaying the field value. Based on the name, we do some field-specific transformations on some fields. Others just get passed on through as text.
var tds = $(content).find("table.viewtable tr > td");
for (var i=0;i<tds.length;i+=2) {This is the field name.
var k = $(tds[i]).text().replace(/:$/, "").replace(/\s+/g, "_").trim().toLowerCase();
switch (k) {"information" is basically synonymous with "website"
case "information":
var link = $(tds[i+1]).find("a");
if (link.length)
obj.website = link[0].attribs.href;
break;"file_size" is exactly what it sounds like, and it has to be turned into a real number.
case "file_size":
obj.size = filesize_parser($(tds[i+1]).text().trim());
break;This is the user that submitted the torrent. We parse it out into the
separate id and name values.
case "submitter":
obj.user = {
id: parseInt($(tds[i+1]).find("a")[0].attribs.href.replace(/^.+?(\d+)$/, "$1"), 10),
name: $(tds[i+1]).text(),
};
break;This might not work on anything except V8. Will have to check that if this ever works on anything except node.
case "date":
obj.date = new Date($(tds[i+1]).text());
break;The "stardom" field just displays the number of people who've set themselves as "fans" of this torrent. I don't really know what the deal is here.
case "stardom":
obj.fans = parseInt($(tds[i+1]).text().replace(/^.+(\d+).+$/, "$1"), 10);
break;All these need to be turned to real numbers instead of strings.
case "seeders":
case "leechers":
case "downloads":
obj[k] = parseInt($(tds[i+1]).text(), 10);
break;Anything not otherwise handled is just sent through as text.
default:
obj[k] = $(tds[i+1]).text();
}
}This is the torrent ID... You already have it, but this seemed like a good idea for completeness.
obj.id = parseInt($(content).find("div.viewdownloadbutton a")[0].attribs.href.replace(/^.+?(\d+)$/, "$1"), 10);This is a chunk of HTML. You'll probably want to turn it into some other kind of markup.
obj.description = $($(content).find("div.viewdescription")[0]).html();
return cb(null, obj);
});
};