Skip to content

Commit

Permalink
write a POC of daemon mode
Browse files Browse the repository at this point in the history
  • Loading branch information
mmgoodnow committed Sep 28, 2020
1 parent b30a49e commit 7d01c94
Show file tree
Hide file tree
Showing 12 changed files with 233 additions and 55 deletions.
18 changes: 18 additions & 0 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions .idea/prettier.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ npm update -g cross-seed
`cross-seed` will look for a configuration file at `~/.cross-seed/config.js`
(`AppData\Local\cross-seed\config.js` on Windows). In the configuration file ,
you can specify all of the same flags you specified on the command line, but
after that, you won't have to specify them on the command line any more.
after that, you won't have to specify them on the command line any more. If you
would like to use a different directory than the default, you can set the
`CONFIG_DIR` environment variable.

To create a configuration file, run

Expand Down
90 changes: 56 additions & 34 deletions src/cmd.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,42 @@
#!/usr/bin/env node

const { program } = require("commander");
const { program, Command } = require("commander");
const chalk = require("chalk");
const packageDotJson = require("../package.json");
const main = require("./index");
const { main } = require("./index");
const { CONFIG, generateConfig } = require("./configuration");
const { clear: clearCache } = require("./cache");
const { serve } = require("./server");

function addSharedOptions() {
return this.requiredOption(
"-u, --jackett-server-url <url>",
"Your Jackett server url",
CONFIG.jackettServerUrl
)
.requiredOption(
"-k, --jackett-api-key <key>",
"Your Jackett API key",
CONFIG.jackettApiKey
)
.requiredOption(
"-t, --trackers <tracker>",
"Comma-separated list of Jackett tracker ids to search",
CONFIG.trackers && CONFIG.trackers.join(",")
)
.requiredOption(
"-i, --torrent-dir <dir>",
"Directory with torrent files",
CONFIG.torrentDir
)
.requiredOption(
"-s, --output-dir <dir>",
"Directory to save results in",
CONFIG.outputDir
);
}
// monkey patch Command with this addSharedOptions function
Command.prototype.addSharedOptions = addSharedOptions;

program.name(packageDotJson.name);
program.description(chalk.yellow.bold("cross-seed"));
Expand All @@ -22,58 +54,48 @@ program
program
.command("clear-cache")
.description("Clear the cache of downloaded-and-rejected torrents")
.action(() => require("./cache").clear());
.action(clearCache);

program
.command("daemon")
.description("Start the cross-serve daemon")
.addSharedOptions()
.action(async (command) => {
const options = command.opts();
options.trackers = options.trackers.split(",").filter((e) => e !== "");
try {
await serve(options);
} catch (e) {
console.error(chalk.bold.red(e.message));
}
});

program
.command("search")
.description("Search for cross-seeds\n")
.addSharedOptions()
.requiredOption(
"-u, --jackett-server-url <url>",
"Your Jackett server url",
CONFIG.jackettServerUrl
)
.requiredOption(
"-k, --jackett-api-key <key>",
"Your Jackett API key",
CONFIG.jackettApiKey
"-o, --offset <offset>",
"Offset to start from",
(n) => parseInt(n),
CONFIG.offset || 0
)
.requiredOption(
"-d, --delay <delay>",
"Pause duration (seconds) between searches",
parseFloat,
CONFIG.delay || 10
)
.requiredOption(
"-t, --trackers <tracker>",
"Comma-separated list of Jackett tracker ids to search",
CONFIG.trackers && CONFIG.trackers.join(",")
)
.requiredOption(
"-i, --torrent-dir <dir>",
"Directory with torrent files",
CONFIG.torrentDir
)
.requiredOption(
"-s, --output-dir <dir>",
"Directory to save results in",
CONFIG.outputDir
)
.requiredOption(
"-o, --offset <offset>",
"Offset to start from",
(n) => parseInt(n),
CONFIG.offset || 0
)
.option(
"-e, --include-episodes",
"Include single-episode torrents in the search",
CONFIG.includeEpisodes || false
)
.action((command) => {
.action(async (command) => {
const options = command.opts();
options.trackers = options.trackers.split(",").filter((e) => e !== "");
try {
main(options);
await main(options);
} catch (e) {
console.error(chalk.bold.red(e.message));
}
Expand Down
7 changes: 7 additions & 0 deletions src/config.template.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
// it here as a default.

module.exports = {
// every time the configuration is changed, this will increment.
configVersion: 1,

jackettServerUrl: "http://localhost:9117/jackett",
jackettApiKey: "YOUR_JACKETT_API_KEY_HERE",

Expand Down Expand Up @@ -31,4 +34,8 @@ module.exports = {

// Whether to search for single episode torrents
includeEpisodes: false,

// added in configVersion 1
//
watchIntervalMins: 10,
};
26 changes: 24 additions & 2 deletions src/configuration.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
const fs = require("fs");
const path = require("path");
const chalk = require('chalk');
const chalk = require("chalk");
const packageDotJson = require("../package.json");
const configTemplate = require("./config.template");
const { CONFIG_TEMPLATE_URL } = require("./constants");

let CONFIG = {};

Expand All @@ -26,8 +28,28 @@ function generateConfig() {
return dest;
}

function printUpdateInstructions(missingKeys) {
const configPath = path.join(appDir(), "config.js");
console.error(chalk.red`
Error: Your configuration file is out of date.
Missing: ${missingKeys.join(", ")}
Please update at ${configPath}.
When you are done, set the configVersion to ${configTemplate.configVersion}.
It may help to read the template, at ${CONFIG_TEMPLATE_URL}
`);
}

try {
CONFIG = require(path.join(appDir(), "config.js"));
const configPath = path.join(appDir(), "config.js");
CONFIG = require(configPath);
const { configVersion = 0 } = CONFIG;
if (configVersion < configTemplate.configVersion) {
const missingKeys = Object.keys(configTemplate).filter(
(k) => !CONFIG.includes(k)
);
printUpdateInstructions(missingKeys);
process.exitCode = 1;
}
} catch (_) {}

module.exports = { CONFIG, appDir, createAppDir, generateConfig };
14 changes: 11 additions & 3 deletions src/constants.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
const EP_REGEX = /^(?<title>.+)[. ](?<season>S\d\d)(?<episode>E\d\d)/i;
const SEASON_REGEX = /^(?<title>.+)[. ](?<season>S\d\d)(?!E\d\d)/i;
const MOVIE_REGEX = /^(?<title>.+)[. ](?<year>\d{4})/i
const MOVIE_REGEX = /^(?<title>.+)[. ](?<year>\d{4})/i;

const EXTENSIONS = ["mkv", "mp4", "avi"];

const CONFIG_TEMPLATE_URL =
"https://github.com/mmgoodnow/cross-seed/blob/master/src/config.template.js";
// because I'm sick of intellij whining at me
const result = {
Link: undefined,
Expand All @@ -12,6 +14,12 @@ const result = {
Title: undefined,
Size: undefined,
Guid: undefined,
}
};

module.exports = { EP_REGEX, SEASON_REGEX, MOVIE_REGEX, EXTENSIONS };
module.exports = {
EP_REGEX,
SEASON_REGEX,
MOVIE_REGEX,
EXTENSIONS,
CONFIG_TEMPLATE_URL,
};
25 changes: 19 additions & 6 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ const fs = require("fs");

const chalk = require("chalk");
const { stripExtension } = require("./utils");
const { loadTorrentDir, saveTorrentFile } = require("./torrent");
const { filterTorrentFile } = require("./preFilter");
const {
loadTorrentDir,
saveTorrentFile,
getInfoHashesToExclude,
getTorrentByName,
} = require("./torrent");
const { filterTorrentFile, filterDupes } = require("./preFilter");
const { assessResult } = require("./decide");
const { makeJackettRequest, validateJackettApi } = require("./jackett");

Expand All @@ -15,7 +20,7 @@ async function findOnOtherSites(info, hashesToExclude, config) {
try {
response = await makeJackettRequest(query, config);
} catch (e) {
console.error(chalk.red`error querying Jackett for ${query}`)
console.error(chalk.red`error querying Jackett for ${query}`);
return 0;
}
const results = response.data.Results;
Expand Down Expand Up @@ -50,12 +55,20 @@ async function findMatchesBatch(samples, hashesToExclude, config) {
return totalFound;
}

async function searchForSingleTorrentByName(name, config) {
const { torrentDir } = config;
const hashesToExclude = getInfoHashesToExclude(torrentDir);
const meta = getTorrentByName(torrentDir, name);
console.log(meta);
return findOnOtherSites(meta, hashesToExclude, config);
}

async function main(config) {
const { torrentDir, offset, outputDir, includeEpisodes } = config;
const parsedTorrents = loadTorrentDir(torrentDir);
const hashesToExclude = parsedTorrents.map((t) => t.infoHash);
const filteredTorrents = parsedTorrents.filter((e, i, a) =>
filterTorrentFile(e, i, a, includeEpisodes)
const filteredTorrents = filterDupes(parsedTorrents).filter(
filterTorrentFile(includeEpisodes)
);
const samples = filteredTorrents.slice(offset);

Expand Down Expand Up @@ -83,4 +96,4 @@ async function main(config) {
);
}

module.exports = main;
module.exports = { main, searchForSingleTorrentByName };
2 changes: 1 addition & 1 deletion src/jackett.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function makeJackettRequest(name, config) {
const params = {
apikey: jackettApiKey,
Query: reformatTitleForSearching(name),
["Tracker[]"]: trackers,
"Tracker[]": trackers,
};

const opts = {
Expand Down
18 changes: 12 additions & 6 deletions src/preFilter.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const path = require("path");
const { EP_REGEX, EXTENSIONS } = require("./constants");

function filterTorrentFile(info, index, arr, includeEpisodes) {
const filterTorrentFile = (includeEpisodes) => (info) => {
const { files } = info;
if (
!includeEpisodes &&
Expand All @@ -12,18 +12,24 @@ function filterTorrentFile(info, index, arr, includeEpisodes) {
}

const allVideos = files.every((file) =>
EXTENSIONS.map(e => `.${e}`).includes(path.extname(file.path))
EXTENSIONS.map((e) => `.${e}`).includes(path.extname(file.path))
);
if (!allVideos) return false;

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

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

return true;
};

function filterDupes(metaFiles) {
return metaFiles.filter((info, index) => {
const firstOccurrence = metaFiles.findIndex(
(e) => e.name === info.name
);
return index === firstOccurrence;
});
}

module.exports = { filterTorrentFile };
module.exports = { filterTorrentFile, filterDupes };
Loading

0 comments on commit 7d01c94

Please sign in to comment.