Skip to content

Commit

Permalink
Add argument parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
mmgoodnow committed Jun 9, 2020
1 parent 51d93c1 commit 9766383
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 30 deletions.
130 changes: 117 additions & 13 deletions index.js
Expand Up @@ -3,19 +3,45 @@
const parseTorrent = require("parse-torrent");
const fs = require("fs");
const path = require("path");
const axios = require("axios").default;
const util = require("util");
const querystring = require("querystring");

const jackettServerUrl = "***REMOVED***";
const axios = require("axios");
const minimist = require("minimist");

const jackettPath = "/api/v2.0/indexers/all/results";
const jackettApiKey = "***REMOVED***";
let jackettServerUrl;
let jackettApiKey;
let torrentDir;
let outputDir;
let delay = 10000;

const parseTorrentRemote = util.promisify(parseTorrent.remote);

function parseCommandLineArgs() {
const options = minimist(process.argv.slice(2));

if (!options._[0]) console.error("specify a directory containing torrents");
if (!options.o) console.error("specify an output directory with -o");
if (!options.u) console.error("specify jackett url with -u");
if (!options.k) console.error("specify jackett api key with -k");
if (!(options.k && options.u && options.o && options._[0])) return false;

jackettServerUrl = options.u;
jackettApiKey = options.k;
torrentDir = options._[0];
outputDir = options.o
delay = (options.d || 10) * 1000

return true;
}

function makeJackettRequest(query) {
const params = querystring.stringify({
apikey: jackettApiKey,
Query: query
Query: query,
});
return axios.get(`${jackettServerUrl}${jackettPath}`, params);
return axios.get(`${jackettServerUrl}${jackettPath}?${params}`);
}

function parseTorrentFromFilename(filename) {
Expand All @@ -24,26 +50,104 @@ function parseTorrentFromFilename(filename) {
return torrentInfo;
}

function filterTorrentFile(info) {
const allMkvs = info.files.every(file => file.path.endsWith(".mkv"));
function filterTorrentFile(info, index, arr) {
const allMkvs = info.files.every((file) => file.path.endsWith(".mkv"));
if (!allMkvs) return false;

const cb = file => file.path.split(path.sep).length <= 2;
const cb = (file) => file.path.split(path.sep).length <= 2;
const notNested = info.files.every(cb);
if (!notNested) return false;

const firstOccurrence = arr.findIndex((e) => e.name === info.name);
if (index !== firstOccurrence) return false;

return true;
}

function main() {
const dirContents = fs.readdirSync("torrents")
.map(fn => path.join("torrents", fn));
function compareFileTrees(a, b) {
if (a.length !== b.length) return false;
const sorter = (m, n) => (m.path < n.path ? -1 : m.path > n.path ? 1 : 0);
const sortedA = a.slice().sort(sorter);
const sortedB = b.slice().sort(sorter);

const cmp = (elOfA, elOfB) => {
const pathsAreEqual = elOfB.path === elOfA.path;
const lengthsAreEqual = elOfB.length === elOfA.length;
return pathsAreEqual && lengthsAreEqual;
};
return sortedA.every((elOfA, i) => cmp(elOfA, sortedB[i]));
}

async function assessJackettResult(result, ogInfo) {
const resultInfo = await parseTorrentRemote(result.Link);
if (resultInfo.length !== ogInfo.length) return null;
const name = ogInfo.name;
const announce1 = ogInfo.announce[0];
const announce2 = resultInfo.announce[0];

if (resultInfo.infoHash === ogInfo.infoHash) {
console.log(`hash match for ${name}: ${announce1}, ${announce2}`);
return null;
}

if (!compareFileTrees(resultInfo.files, ogInfo.files)) {
console.log(`trees differ for ${name}: ${announce1}, ${announce2}}`);
return null;
}

return {
tracker: result.TrackerId,
info: resultInfo,
};
}

async function findOnOtherSites(info) {
const response = await makeJackettRequest(info.name);
const results = response.data.Results;
const promises = results.map((result) => assessJackettResult(result, info));
const finished = await Promise.all(promises);
finished
.filter((e) => e !== null)
.forEach(({ tracker, info: { name } }) => {
console.log(`Found ${name} on ${tracker}`);
saveTorrentFile(tracker, info);
});
}

function saveTorrentFile(tracker, info) {
const buf = parseTorrent.toTorrentFile(info);
const filename = `[${tracker}]${info.name}.torrent`;
fs.writeFileSync(path.join("x-seeds", filename), buf, {
mode: 0o644,
});
}

async function main() {

const successfulParse = parseCommandLineArgs();
if (!successfulParse) return;

const dirContents = fs
.readdirSync(torrentDir)
.map((fn) => path.join(torrentDir, fn));
const parsedTorrents = dirContents.map(parseTorrentFromFilename);
const filteredTorrents = parsedTorrents.filter(filterTorrentFile);

const samples = filteredTorrents.slice(0, 4);
console.log(samples.map(t => t.name));
console.log(
"Found %d torrents, %d suitable",
dirContents.length,
filteredTorrents.length
);

fs.mkdirSync(outputDir, {
recursive: true,
});
const samples = filteredTorrents.slice(0, 16);
for (sample of samples) {
console.log(`Searching for ${sample.name}...`);
await new Promise((r) => setTimeout(r, delay));
await findOnOtherSites(sample);
}
}

main();
40 changes: 23 additions & 17 deletions package.json
@@ -1,19 +1,25 @@
{
"name": "cross-seed",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^0.19.2",
"parse-torrent": "^7.1.3"
},
"type": "module",
"devDependencies": {
"prettier": "^2.0.5"
}
"name": "cross-seed",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^0.19.2",
"minimist": "^1.2.5",
"parse-torrent": "^7.1.3"
},
"type": "module",
"devDependencies": {
"prettier": "^2.0.5"
},
"prettier": {
"tabWidth": 4,
"useTabs": true,
"htmlWhitespaceSensitivity": "ignore"
}
}
5 changes: 5 additions & 0 deletions yarn.lock
Expand Up @@ -60,6 +60,11 @@ mimic-response@^2.0.0:
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43"
integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==

minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==

ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
Expand Down

0 comments on commit 9766383

Please sign in to comment.