From b6265c2245a5022fb487f6b80a83a8bb0486447e Mon Sep 17 00:00:00 2001 From: Afraaz Ali Date: Wed, 28 Jul 2021 08:09:58 -0700 Subject: [PATCH 1/4] [FEATURE] - Allow daemon mode to search for torrents via hash * Fixes #118 --- src/pipeline.ts | 10 ++++++++++ src/server.ts | 30 +++++++++++++++++++++++------- src/torrent.ts | 13 +++++++++++++ 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/pipeline.ts b/src/pipeline.ts index 0293bd58..aeb8c10a 100755 --- a/src/pipeline.ts +++ b/src/pipeline.ts @@ -12,6 +12,7 @@ import { Searchee } from "./searchee"; import { getInfoHashesToExclude, getTorrentByName, + getTorrentByHash, loadTorrentDirLight, saveTorrentFile, } from "./torrent"; @@ -145,6 +146,15 @@ export async function searchForSingleTorrentByName( return findOnOtherSites(meta, hashesToExclude); } +export async function searchForSingleTorrentByHash( + hash: string +): Promise { + const meta = await getTorrentByHash(hash); + const hashesToExclude = getInfoHashesToExclude(); + if (!filterByContent(meta)) return null; + return findOnOtherSites(meta, hashesToExclude); +} + async function findSearchableTorrents() { const { offset } = getRuntimeConfig(); const parsedTorrents: Searchee[] = await loadTorrentDirLight(); diff --git a/src/server.ts b/src/server.ts index 735ca895..b37e77ec 100644 --- a/src/server.ts +++ b/src/server.ts @@ -3,7 +3,10 @@ import http from "http"; import qs from "querystring"; import { validateJackettApi } from "./jackett"; import { Label, logger } from "./logger"; -import { searchForSingleTorrentByName } from "./pipeline"; +import { + searchForSingleTorrentByName, + searchForSingleTorrentByHash, +} from "./pipeline"; import { getRuntimeConfig } from "./runtimeConfig"; function getData(req) { @@ -23,7 +26,7 @@ function parseData(data) { return JSON.parse(data); } catch (_) { const parsed = qs.parse(data); - if ("name" in parsed) return parsed; + if ("name" in parsed || "hash" in parsed) return parsed; throw new Error(`Unable to parse request body: "${data}"`); } } @@ -40,21 +43,34 @@ async function handleRequest(req, res) { return; } const dataStr = await getData(req); - const { name } = parseData(dataStr); + const { name, hash } = parseData(dataStr); + const criteria = name ? name : hash; + const message = `Received ${name ? "name" : "hash"} ${criteria}`; res.writeHead(204); res.end(); - logger.info({ label: Label.SERVER, message: `Received name ${name}` }); + + logger.info({ label: Label.SERVER, message }); + try { - const numFound = await searchForSingleTorrentByName(name); + let numFound = null; + if (name) { + numFound = await searchForSingleTorrentByName(name); + } + + // Just in case both name and hash are passed. + if (hash && !numFound) { + numFound = await searchForSingleTorrentByHash(hash); + } + if (numFound === null) { logger.info({ label: Label.SERVER, - message: `Did not search for ${name}`, + message: `Did not search for ${criteria}`, }); } else { logger.info({ label: Label.SERVER, - message: `Found ${numFound} torrents for ${name}`, + message: `Found ${numFound} torrents for ${criteria}`, }); } } catch (e) { diff --git a/src/torrent.ts b/src/torrent.ts index f8d7d3e6..1c54b083 100644 --- a/src/torrent.ts +++ b/src/torrent.ts @@ -156,3 +156,16 @@ export async function getTorrentByName(name: string): Promise { } return parseTorrentFromFilename(findResult.filepath); } + +export async function getTorrentByHash(hash: string): Promise { + await indexNewTorrents(); + const findResult = db + .get(INDEXED_TORRENTS) + .value() + .find((e) => e.infoHash === hash); + if (findResult === undefined) { + const message = `could not find a torrent with the hash ${hash}`; + throw new Error(message); + } + return parseTorrentFromFilename(findResult.filepath); +} From 6036ad4f34f03ab5a41a6f8c218b780da9161074 Mon Sep 17 00:00:00 2001 From: Afraaz Ali Date: Wed, 28 Jul 2021 11:00:44 -0700 Subject: [PATCH 2/4] [FEATURE] - Allow daemon mode to search for torrents via hash * Pass key/value pair for daemon mode searching --- src/pipeline.ts | 19 +++++-------------- src/server.ts | 25 +++++++++++++++---------- src/torrent.ts | 30 +++++++++++++++--------------- 3 files changed, 35 insertions(+), 39 deletions(-) diff --git a/src/pipeline.ts b/src/pipeline.ts index aeb8c10a..4d13ac47 100755 --- a/src/pipeline.ts +++ b/src/pipeline.ts @@ -10,9 +10,9 @@ import { filterByContent, filterDupes, filterTimestamps } from "./preFilter"; import { getRuntimeConfig } from "./runtimeConfig"; import { Searchee } from "./searchee"; import { + TorrentInfo, getInfoHashesToExclude, - getTorrentByName, - getTorrentByHash, + getTorrentByNameOrHash, loadTorrentDirLight, saveTorrentFile, } from "./torrent"; @@ -137,19 +137,10 @@ async function findMatchesBatch( return totalFound; } -export async function searchForSingleTorrentByName( - name: string +export async function searchForSingleTorrentByNameOrHash( + torrent: TorrentInfo ): Promise { - const meta = await getTorrentByName(name); - const hashesToExclude = getInfoHashesToExclude(); - if (!filterByContent(meta)) return null; - return findOnOtherSites(meta, hashesToExclude); -} - -export async function searchForSingleTorrentByHash( - hash: string -): Promise { - const meta = await getTorrentByHash(hash); + const meta = await getTorrentByNameOrHash(torrent); const hashesToExclude = getInfoHashesToExclude(); if (!filterByContent(meta)) return null; return findOnOtherSites(meta, hashesToExclude); diff --git a/src/server.ts b/src/server.ts index b37e77ec..9737e149 100644 --- a/src/server.ts +++ b/src/server.ts @@ -3,10 +3,7 @@ import http from "http"; import qs from "querystring"; import { validateJackettApi } from "./jackett"; import { Label, logger } from "./logger"; -import { - searchForSingleTorrentByName, - searchForSingleTorrentByHash, -} from "./pipeline"; +import { searchForSingleTorrentByNameOrHash } from "./pipeline"; import { getRuntimeConfig } from "./runtimeConfig"; function getData(req) { @@ -45,6 +42,16 @@ async function handleRequest(req, res) { const dataStr = await getData(req); const { name, hash } = parseData(dataStr); const criteria = name ? name : hash; + + if (!criteria) { + logger.error({ + label: Label.SERVER, + message: "A name or info hash must be provided", + }); + res.writeHead(422); + res.end(); + } + const message = `Received ${name ? "name" : "hash"} ${criteria}`; res.writeHead(204); res.end(); @@ -54,12 +61,10 @@ async function handleRequest(req, res) { try { let numFound = null; if (name) { - numFound = await searchForSingleTorrentByName(name); - } - - // Just in case both name and hash are passed. - if (hash && !numFound) { - numFound = await searchForSingleTorrentByHash(hash); + numFound = await searchForSingleTorrentByNameOrHash({ + name, + infoHash: hash, + }); } if (numFound === null) { diff --git a/src/torrent.ts b/src/torrent.ts index 1c54b083..2f05bbba 100644 --- a/src/torrent.ts +++ b/src/torrent.ts @@ -10,6 +10,11 @@ import { getRuntimeConfig } from "./runtimeConfig"; import { createSearcheeFromTorrentFile, Searchee } from "./searchee"; import { ok, stripExtension } from "./utils"; +export interface TorrentInfo { + infoHash?: string; + name?: string; +} + export async function parseTorrentFromFilename( filename: string ): Promise { @@ -144,27 +149,22 @@ export async function loadTorrentDirLight(): Promise { ).then((searcheeResults) => searcheeResults.filter(ok)); } -export async function getTorrentByName(name: string): Promise { +export async function getTorrentByNameOrHash( + torrentInfo: TorrentInfo +): Promise { await indexNewTorrents(); - const findResult = db - .get(INDEXED_TORRENTS) - .value() - .find((e) => e.name === name); - if (findResult === undefined) { - const message = `could not find a torrent with the name ${name}`; - throw new Error(message); - } - return parseTorrentFromFilename(findResult.filepath); -} -export async function getTorrentByHash(hash: string): Promise { - await indexNewTorrents(); + const criteria = torrentInfo?.infoHash + ? torrentInfo?.infoHash + : torrentInfo?.name; + const property = torrentInfo?.infoHash ? "infoHash" : "name"; + const findResult = db .get(INDEXED_TORRENTS) .value() - .find((e) => e.infoHash === hash); + .find((e) => e[property] === criteria); if (findResult === undefined) { - const message = `could not find a torrent with the hash ${hash}`; + const message = `could not find a torrent with the name ${criteria}`; throw new Error(message); } return parseTorrentFromFilename(findResult.filepath); From 77223e84c7ce03422c3f268e9554395d4ee1f392 Mon Sep 17 00:00:00 2001 From: Michael Goodnow Date: Fri, 30 Jul 2021 01:24:40 -0400 Subject: [PATCH 3/4] Update naming --- src/pipeline.ts | 10 +++++----- src/server.ts | 42 ++++++++++++++++++++++++------------------ src/torrent.ts | 18 +++++------------- 3 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/pipeline.ts b/src/pipeline.ts index 4d13ac47..e572a704 100755 --- a/src/pipeline.ts +++ b/src/pipeline.ts @@ -10,9 +10,9 @@ import { filterByContent, filterDupes, filterTimestamps } from "./preFilter"; import { getRuntimeConfig } from "./runtimeConfig"; import { Searchee } from "./searchee"; import { - TorrentInfo, + TorrentLocator, getInfoHashesToExclude, - getTorrentByNameOrHash, + getTorrentByCriteria, loadTorrentDirLight, saveTorrentFile, } from "./torrent"; @@ -137,10 +137,10 @@ async function findMatchesBatch( return totalFound; } -export async function searchForSingleTorrentByNameOrHash( - torrent: TorrentInfo +export async function searchForLocalTorrentByCriteria( + criteria: TorrentLocator ): Promise { - const meta = await getTorrentByNameOrHash(torrent); + const meta = await getTorrentByCriteria(criteria); const hashesToExclude = getInfoHashesToExclude(); if (!filterByContent(meta)) return null; return findOnOtherSites(meta, hashesToExclude); diff --git a/src/server.ts b/src/server.ts index 9737e149..637bdbd7 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,11 +1,11 @@ import fs from "fs"; import http from "http"; import qs from "querystring"; -import { validateJackettApi } from "./jackett"; import { Label, logger } from "./logger"; -import { searchForSingleTorrentByNameOrHash } from "./pipeline"; +import { searchForLocalTorrentByCriteria } from "./pipeline"; import { getRuntimeConfig } from "./runtimeConfig"; - +import { TorrentLocator } from "./torrent"; +import { inspect } from "util"; function getData(req) { return new Promise((resolve) => { const chunks = []; @@ -19,13 +19,21 @@ function getData(req) { } function parseData(data) { + let parsed; try { - return JSON.parse(data); + parsed = JSON.parse(data); } catch (_) { - const parsed = qs.parse(data); - if ("name" in parsed || "hash" in parsed) return parsed; - throw new Error(`Unable to parse request body: "${data}"`); + parsed = qs.parse(data); + } + + if ("infoHash" in parsed) { + parsed.infoHash = parsed.infoHash.toLowerCase(); + } + if ("name" in parsed || "infoHash" in parsed) { + return parsed; } + + throw new Error(`Unable to parse request body: "${data}"`); } async function handleRequest(req, res) { @@ -40,19 +48,20 @@ async function handleRequest(req, res) { return; } const dataStr = await getData(req); - const { name, hash } = parseData(dataStr); - const criteria = name ? name : hash; + const criteria: TorrentLocator = parseData(dataStr); if (!criteria) { logger.error({ label: Label.SERVER, message: "A name or info hash must be provided", }); - res.writeHead(422); + res.writeHead(400); res.end(); } - const message = `Received ${name ? "name" : "hash"} ${criteria}`; + const criteriaStr = inspect({ ...criteria }); + + const message = `Received ${criteriaStr}`; res.writeHead(204); res.end(); @@ -60,22 +69,19 @@ async function handleRequest(req, res) { try { let numFound = null; - if (name) { - numFound = await searchForSingleTorrentByNameOrHash({ - name, - infoHash: hash, - }); + if (criteria) { + numFound = await searchForLocalTorrentByCriteria(criteria); } if (numFound === null) { logger.info({ label: Label.SERVER, - message: `Did not search for ${criteria}`, + message: `Did not search for ${criteriaStr}`, }); } else { logger.info({ label: Label.SERVER, - message: `Found ${numFound} torrents for ${criteria}`, + message: `Found ${numFound} torrents for ${criteriaStr}`, }); } } catch (e) { diff --git a/src/torrent.ts b/src/torrent.ts index 2f05bbba..3b31e44d 100644 --- a/src/torrent.ts +++ b/src/torrent.ts @@ -10,7 +10,7 @@ import { getRuntimeConfig } from "./runtimeConfig"; import { createSearcheeFromTorrentFile, Searchee } from "./searchee"; import { ok, stripExtension } from "./utils"; -export interface TorrentInfo { +export interface TorrentLocator { infoHash?: string; name?: string; } @@ -149,22 +149,14 @@ export async function loadTorrentDirLight(): Promise { ).then((searcheeResults) => searcheeResults.filter(ok)); } -export async function getTorrentByNameOrHash( - torrentInfo: TorrentInfo +export async function getTorrentByCriteria( + criteria: TorrentLocator ): Promise { await indexNewTorrents(); - const criteria = torrentInfo?.infoHash - ? torrentInfo?.infoHash - : torrentInfo?.name; - const property = torrentInfo?.infoHash ? "infoHash" : "name"; - - const findResult = db - .get(INDEXED_TORRENTS) - .value() - .find((e) => e[property] === criteria); + const findResult = db.get(INDEXED_TORRENTS).find(criteria).value(); if (findResult === undefined) { - const message = `could not find a torrent with the name ${criteria}`; + const message = `could not find a torrent with the criteria`; throw new Error(message); } return parseTorrentFromFilename(findResult.filepath); From c6d94451f61205ae00540c4910c9fd0e0fadc9f8 Mon Sep 17 00:00:00 2001 From: Michael Goodnow Date: Fri, 30 Jul 2021 01:26:18 -0400 Subject: [PATCH 4/4] Fix missing log parameter --- src/server.ts | 3 ++- src/torrent.ts | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/server.ts b/src/server.ts index 637bdbd7..07fd9eae 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,11 +1,12 @@ import fs from "fs"; import http from "http"; import qs from "querystring"; +import { inspect } from "util"; import { Label, logger } from "./logger"; import { searchForLocalTorrentByCriteria } from "./pipeline"; import { getRuntimeConfig } from "./runtimeConfig"; import { TorrentLocator } from "./torrent"; -import { inspect } from "util"; + function getData(req) { return new Promise((resolve) => { const chunks = []; diff --git a/src/torrent.ts b/src/torrent.ts index 3b31e44d..0c7231e9 100644 --- a/src/torrent.ts +++ b/src/torrent.ts @@ -2,6 +2,7 @@ import fs, { promises as fsPromises } from "fs"; import parseTorrent, { Metafile } from "parse-torrent"; import path from "path"; import { concat } from "simple-get"; +import { inspect } from "util"; import { INDEXED_TORRENTS } from "./constants"; import db from "./db"; import { CrossSeedError } from "./errors"; @@ -156,7 +157,9 @@ export async function getTorrentByCriteria( const findResult = db.get(INDEXED_TORRENTS).find(criteria).value(); if (findResult === undefined) { - const message = `could not find a torrent with the criteria`; + const message = `could not find a torrent with the criteria ${inspect( + criteria + )}`; throw new Error(message); } return parseTorrentFromFilename(findResult.filepath);