diff --git a/package-lock.json b/package-lock.json index 918ddcf..36ab843 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "@types/parse-torrent": "^5.8.7", "@typescript-eslint/eslint-plugin": "^5.44.0", "@typescript-eslint/parser": "^5.44.0", + "airdcpp-apisocket": "^2.4.4", "axios": "^1.5.0", "concurrently": "^7.6.0", "cross-env": "^7.0.3", @@ -36,7 +37,8 @@ "qbittorrent-api-v2": "^1.2.2", "ts-jest": "^29.0.3", "ts-node": "^10.9.1", - "typescript": "^4.9.3" + "typescript": "^4.9.3", + "ws": "^8.16.0" }, "engines": { "node": ">= 16.x.x" @@ -1759,6 +1761,19 @@ "node": ">=0.4.0" } }, + "node_modules/airdcpp-apisocket": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/airdcpp-apisocket/-/airdcpp-apisocket-2.4.4.tgz", + "integrity": "sha512-Xn0kWSVdLJwPpOoHcdI2wzzfzZW2jTpuyZR2wCNs2UIlZhO+FTwMf3QQfNCt5gYTOld9LaiCEulxFuXDA8qrLA==", + "dev": true, + "dependencies": { + "chalk": "^4.1.2", + "events": "^3.3.0", + "invariant": "^2.2.4", + "is-in-browser": "^2.0.0", + "promise": "^8.1.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -2061,6 +2076,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -3694,6 +3715,15 @@ "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==" }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -3881,9 +3911,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "dev": true, "funding": [ { @@ -4387,6 +4417,15 @@ "node": ">= 0.4" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/ioredis": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz", @@ -4544,6 +4583,12 @@ "node": ">=0.10.0" } }, + "node_modules/is-in-browser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-2.0.0.tgz", + "integrity": "sha512-/NUv5pqj+krUJalhGpj0lyy+x7vrD9jt1PlAfkoIDEXqE+xZgFJ4FU8e9m99WuHbCqsBZVf+nzvAjNso+SO80A==", + "dev": true + }, "node_modules/is-interactive": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", @@ -5521,6 +5566,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -6485,6 +6542,15 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dev": true, + "dependencies": { + "asap": "~2.0.6" + } + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -7960,6 +8026,27 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/services/autodownload.service.ts b/services/autodownload.service.ts new file mode 100644 index 0000000..4e5753a --- /dev/null +++ b/services/autodownload.service.ts @@ -0,0 +1,68 @@ +"use strict"; +import { Context, Service, ServiceBroker, ServiceSchema, Errors } from "moleculer"; +import axios from "axios"; + +export default class AutoDownloadService extends Service { + // @ts-ignore + public constructor( + public broker: ServiceBroker, + schema: ServiceSchema<{}> = { name: "autodownload" }, + ) { + super(broker); + this.parseServiceSchema({ + name: "autodownload", + mixins: [], + hooks: {}, + actions: { + searchWantedComics: { + rest: "POST /searchWantedComics", + handler: async (ctx: Context<{}>) => { + // 1.iterate through the wanted comic objects, and: + // 1a. Orchestrate all issues from ComicVine if the entire volume is wanted + // 1b. Just the issues in "wanted.issues[]" + const wantedComics: any = await this.broker.call( + "library.getComicsMarkedAsWanted", + {}, + ); + + // Iterate through the list of wanted comics + for (const comic of wantedComics) { + let issuesToSearch: any = []; + + if (comic.wanted.markEntireVolumeAsWanted) { + // 1a. Fetch all issues from ComicVine if the entire volume is wanted + issuesToSearch = await this.broker.call( + "comicvine.getIssuesForVolume", + { + volumeId: comic.wanted.volume.id, + }, + ); + } else if (comic.wanted.issues && comic.wanted.issues.length > 0) { + // 1b. Just the issues in "wanted.issues[]" + issuesToSearch = comic.wanted.issues; + } + for (const issue of issuesToSearch) { + // construct the search queries + } + } + }, + }, + determineDownloadChannel: { + rest: "POST /determineDownloadChannel", + handler: async (ctx: Context<{}>) => { + // 1. Parse the incoming search query + // to make sure that it is well-formed + // At the very least, it should have name, year, number + // 2. Choose between download mediums based on user-preference? + // possible choices are: DC++, Torrent + // 3. Perform the search on those media with the aforementioned search query + // 4. Choose a subset of relevant search results, + // and score them + // 5. Download the highest-scoring, relevant result + }, + }, + }, + methods: {}, + }); + } +} diff --git a/services/prowlarr.service.ts b/services/prowlarr.service.ts index b89359c..0a1b7a9 100644 --- a/services/prowlarr.service.ts +++ b/services/prowlarr.service.ts @@ -65,6 +65,7 @@ export default class ProwlarrService extends Service { offset: number; }>, ) => { + console.log(JSON.stringify(ctx.params, null, 2)); const { indexerIds, categories, @@ -76,15 +77,16 @@ export default class ProwlarrService extends Service { limit, offset, } = ctx.params; - + const indexer = indexerIds[0] ? indexerIds.length === 1 : indexerIds; + const category = categories[0] ? categories.length === 1 : categories; const result = await axios({ url: `http://${host}:${port}/api/v1/search`, method: "GET", params: { query, type, - indexerIds, - categories, + indexer, + category, limit, offset, }, diff --git a/services/qbittorrent.service.ts b/services/qbittorrent.service.ts index aa6b0d7..8239b7a 100644 --- a/services/qbittorrent.service.ts +++ b/services/qbittorrent.service.ts @@ -86,7 +86,6 @@ export default class QBittorrentService extends Service { getClientInfo: { rest: "GET /getClientInfo", handler: async (ctx: Context<{}>) => { - console.log(this.meta.app); await this.broker.call("qbittorrent.loginWithStoredCredentials", {}); return { buildInfo: await this.meta.app.buildInfo(), @@ -212,6 +211,20 @@ export default class QBittorrentService extends Service { } }, }, + determineDownloadApps: { + rest: "", + handler: async () => { + // 1. Parse the incoming search query + // to make sure that it is well-formed + // At the very least, it should have name, year, number + // 2. Choose between download mediums based on user-preference? + // possible choices are: DC++, Torrent + // 3. Perform the search on those media with the aforementioned search query + // 4. Choose a subset of relevant search results, + // and score them + // 5. Download the highest-scoring, relevant result + }, + }, }, methods: {}, async started() {