From f3dd0aea7ec23cfcfa29953179256051c14f6769 Mon Sep 17 00:00:00 2001 From: Kevin Midboe Date: Sun, 2 Oct 2022 10:25:05 +0200 Subject: [PATCH] Consistent error rejection & resolve around spawn calls. --- src/pirate/pirateRepository.js | 199 ++++++++++++------ src/webserver/controllers/pirate/addMagnet.js | 7 +- .../controllers/pirate/searchTheBay.js | 5 +- 3 files changed, 140 insertions(+), 71 deletions(-) diff --git a/src/pirate/pirateRepository.js b/src/pirate/pirateRepository.js index 86748f7..b24ba2e 100644 --- a/src/pirate/pirateRepository.js +++ b/src/pirate/pirateRepository.js @@ -5,55 +5,117 @@ import { spawn } from "child_process"; import establishedDatabase from "../database/database.js"; import cache from "../cache/redis.js"; -function getMagnetFromURL(url) { - return new Promise(resolve => { - const options = new URL(url); - if (options.protocol.includes("magnet")) resolve(url); +class SearchPackageNotFoundError extends Error { + constructor() { + const message = "Search is not setup, view logs."; + super(message); + const warningMessage = `Warning! Package 'torrentSearch' not setup! View project README.`; + console.log(warningMessage); /* eslint-disable-line no-console */ + } +} + +class AddMagnetPackageNotFoundError extends Error { + constructor() { + const message = "Adding magnet is not setup, view logs."; + super(message); + + const warningMessage = `Warning! Package 'delugeClient' not setup! View project README.`; + console.log(warningMessage); /* eslint-disable-line no-console */ + } +} + +class InvalidMagnetUrlError extends Error { + constructor() { + const message = "Invalid magnet url."; + super(message); + } +} + +class UnexpectedScriptError extends Error { + constructor(_package, error = null) { + const message = `There was an unexpected error while running package: ${_package}`; + super(message); + this.error = error; + + // console.log("Unexpected script error:", error); + } +} + +function getMagnetFromURL(url) { + const options = new URL(url); + if (options?.protocol?.includes("magnet")) return Promise.resolve(url); + + return new Promise((resolve, reject) => { http.get(options, res => { - if (res.statusCode === 301 || res.statusCode === 302) { - resolve(res.headers.location); - } + if (res.statusCode !== 301 && res.statusCode !== 302) + reject(new InvalidMagnetUrlError()); + if (!res?.headers?.location?.includes("magnet")) + reject(new InvalidMagnetUrlError()); + + return resolve(res.headers.location); }); }); } function removeNewLineListItem(list) { - return list.filter(el => !el.includes("\n")); + return list.map(el => el.replace("\n", "")).filter(el => el.length !== 0); } -async function find(searchterm, callback) { - let data = []; +function decodeBufferListToString(bufferList) { + let data = bufferList.map(bufferElement => bufferElement.toString()); + if (data.length === 0) return null; + + data = removeNewLineListItem(data); + return data.join(""); +} + +function addMagnetScript(magnet, callback) { + const data = []; + let error = null; + const args = ["add", magnet]; + + const addMagnet = spawn("delugeclient", args); + + addMagnet.stdout.on("data", bufferedData => data.push(bufferedData)); + addMagnet.stderr.on("data", bufferedError => { + error = bufferedError.toString(); + }); + + addMagnet.on("exit", () => callback(error, decodeBufferListToString(data))); + addMagnet.on("error", error => { + callback(error); + }); +} + +function handleAddMagnetScriptError(error) { + if (error?.code === "ENOENT") return new AddMagnetPackageNotFoundError(); + + return new UnexpectedScriptError("delugeClient", error); +} + +function searchScript(searchterm, callback) { + const data = []; + let error = null; const args = [searchterm, "-s", "jackett", "--print"]; + const torrentSearch = spawn("torrentsearch", args); - torrentSearch.stdout.on("data", d => { - console.log("got data, appending:", d); - data.push(d.toString()); + torrentSearch.stdout.on("data", bufferedData => data.push(bufferedData)); + torrentSearch.stderr.on("data", bufferedError => { + error = bufferedError.toString(); }); - torrentSearch.on("exit", () => { - data = removeNewLineListItem(data); - data = data.join(""); - console.log("returning to callback:", data); - - callback(null, data); - }); - - PythonShell.run("torrentsearch", options, callback); - // PythonShell does not support return + torrentSearch.on("exit", () => + callback(error, decodeBufferListToString(data)) + ); + torrentSearch.on("error", error => callback(error)); } -async function callPythonAddMagnet(url, callback) { - getMagnetFromURL(url) - .then(magnet => { - const options = { args: ["add", magnet] }; +function handleSearchScriptError(error) { + if (error?.code === "ENOENT") return new SearchPackageNotFoundError(); - PythonShell.run("delugeClient", options, callback); - }) - .catch(err => { - throw new Error(err); - }); + return new UnexpectedScriptError("torrentSearch", error); } export async function SearchPiratebay(_query) { @@ -64,47 +126,46 @@ export async function SearchPiratebay(_query) { } const cacheKey = `pirate/${query}`; + try { + const hit = await cache.get(cacheKey); - return new Promise((resolve, reject) => - cache - .get(cacheKey) - .then(resolve) - .catch(() => - find(query, (err, results) => { - if (err) { - console.log("THERE WAS A FUCKING ERROR!\n", err); // eslint-disable-line no-console - reject(Error("There was a error when searching for torrents")); - } + if (hit) { + return Promise.resolve(hit); + } + } catch (_) {} - if (results) { - const jsonData = JSON.parse(results, null, "\t"); - cache.set(cacheKey, jsonData); - resolve(jsonData); - } - }) - ) - ); + return new Promise((resolve, reject) => { + searchScript(query, (error, results) => { + if (error || !results) return reject(handleSearchScriptError(error)); + + const jsonData = JSON.parse(results, null, "\t"); + cache.set(cacheKey, jsonData); + return resolve(jsonData); + }); + }); } -export function AddMagnet(magnet, name, tmdbId) { - return new Promise((resolve, reject) => - callPythonAddMagnet(magnet, (err, results) => { - if (err) { - /* eslint-disable no-console */ - console.log(err); - reject(Error("Enable to add torrent", err)); - } - /* eslint-disable no-console */ - console.log("result/error:", err, results); +export async function AddMagnet(magnetUrl, name, tmdbId) { + const magnet = await getMagnetFromURL(magnetUrl); + const insertRequestedMagnetQuery = + "INSERT INTO requested_torrent(magnet, torrent_name, tmdb_id) VALUES (?,?,?)"; + return new Promise((resolve, reject) => { + addMagnetScript(magnet, (error, result) => { + if (error || !result) return reject(handleAddMagnetScriptError(error)); + + const magnetHash = result; // TODO save to database const database = establishedDatabase; - const insertQuery = - "INSERT INTO requested_torrent(magnet,torrent_name,tmdb_id) VALUES (?,?,?)"; - - const response = database.run(insertQuery, [magnet, name, tmdbId]); - console.log(`Response from requsted_torrent insert: ${response}`); - - resolve({ success: true }); - }) - ); + return database + .run(insertRequestedMagnetQuery, [magnet, name, tmdbId]) + .catch(error => reject(error)) + .then(() => + resolve({ + success: true, + message: "Successfully added magnet", + hash: magnetHash + }) + ); + }); + }); } diff --git a/src/webserver/controllers/pirate/addMagnet.js b/src/webserver/controllers/pirate/addMagnet.js index 67d5dae..0d6c1cc 100644 --- a/src/webserver/controllers/pirate/addMagnet.js +++ b/src/webserver/controllers/pirate/addMagnet.js @@ -14,7 +14,12 @@ function addMagnet(req, res) { AddMagnet(magnet, name, tmdbId) .then(result => res.send(result)) .catch(error => { - res.status(500).send({ success: false, message: error.message }); + res + .status(error?.statusCode || 500) + .send({ + success: false, + message: error?.message || "Unexpected error while adding magnet." + }); }); } diff --git a/src/webserver/controllers/pirate/searchTheBay.js b/src/webserver/controllers/pirate/searchTheBay.js index 34b6f07..df4e388 100644 --- a/src/webserver/controllers/pirate/searchTheBay.js +++ b/src/webserver/controllers/pirate/searchTheBay.js @@ -22,7 +22,10 @@ function updateRequested(req, res) { res.send({ success: true, results: result }); }) .catch(error => { - res.status(401).send({ success: false, message: error.message }); + res.status(error?.statusCode || 500).send({ + success: false, + message: error?.message || "Unexpected error while searching." + }); }); }