From e49c9c985a2d2d680cdb4fc8c242ff274dc9009e Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Fri, 1 Sep 2023 11:23:42 -0500 Subject: [PATCH 01/12] =?UTF-8?q?=F0=9F=8C=8A=20Added=20connect=20endpoint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 49 +++++++++++++++++++++++++++++++++ package.json | 19 +++++++------ services/qbittorrent.service.ts | 7 +++++ 3 files changed, 66 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index b665b57..cb2d742 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "threetwo-acquisition-service", "version": "1.0.0", "dependencies": { + "@robertklep/qbittorrent": "^1.0.1", "ioredis": "^5.0.0", "moleculer": "^0.14.27", "moleculer-web": "^0.10.5" @@ -1322,6 +1323,14 @@ "node": ">= 8" } }, + "node_modules/@robertklep/qbittorrent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@robertklep/qbittorrent/-/qbittorrent-1.0.1.tgz", + "integrity": "sha512-XQDKXN8sVGAIoZa+ZeGA+2p1HlQ2QPkfOtq9CL6za1qYsX2aLakkFVB7n4+ifaQoMvtV7l3RWqa/0lKwoUGR2Q==", + "dependencies": { + "needle": "^3.0.0" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -5691,6 +5700,41 @@ "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "dev": true }, + "node_modules/needle": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz", + "integrity": "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==", + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/needle/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/next-tick": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", @@ -6571,6 +6615,11 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", diff --git a/package.json b/package.json index 1dcf26a..b86abcd 100644 --- a/package.json +++ b/package.json @@ -21,31 +21,32 @@ ], "author": "", "devDependencies": { + "@jest/globals": "^29.3.1", + "@types/jest": "^29.2.3", + "@types/node": "^18.11.9", "@typescript-eslint/eslint-plugin": "^5.44.0", "@typescript-eslint/parser": "^5.44.0", + "concurrently": "^7.6.0", + "cross-env": "^7.0.3", "eslint": "^8.28.0", "eslint-config-airbnb-base": "^15.0.0", "eslint-config-airbnb-typescript": "^17.0.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-jest": "^27.1.6", - "prettier": "^2.8.0", - "@jest/globals": "^29.3.1", - "@types/jest": "^29.2.3", - "@types/node": "^18.11.9", - "concurrently": "^7.6.0", - "cross-env": "^7.0.3", "jest": "^29.3.1", "moleculer-repl": "^0.7.3", + "prettier": "^2.8.0", "ts-jest": "^29.0.3", "ts-node": "^10.9.1", "typescript": "^4.9.3" }, "dependencies": { - "moleculer-web": "^0.10.5", + "@robertklep/qbittorrent": "^1.0.1", "ioredis": "^5.0.0", - "moleculer": "^0.14.27" -}, + "moleculer": "^0.14.27", + "moleculer-web": "^0.10.5" + }, "engines": { "node": ">= 16.x.x" } diff --git a/services/qbittorrent.service.ts b/services/qbittorrent.service.ts index 72f7368..ccc6a56 100644 --- a/services/qbittorrent.service.ts +++ b/services/qbittorrent.service.ts @@ -1,5 +1,6 @@ "use strict"; import { Context, Service, ServiceBroker, ServiceSchema, Errors } from "moleculer"; +import { qBittorrentClient } from "@robertklep/qbittorrent"; export default class QBittorrentService extends Service { // @ts-ignore @@ -16,6 +17,12 @@ export default class QBittorrentService extends Service { connect: { rest: "POST /connect", handler: async (ctx: Context<{}>) => { + const client = new qBittorrentClient( + "http://127.0.0.1:8080", + "admin", + "adminadmin", + ); + console.log(client); return { foo: "bar" }; }, }, From 104e7ba0bbb45af0f5a3e99468ee0d5c5dbb6a08 Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Fri, 15 Sep 2023 15:50:06 -0400 Subject: [PATCH 02/12] =?UTF-8?q?=F0=9F=8F=97=EF=B8=8F=20Parameterized=20c?= =?UTF-8?q?onnect=20endpoint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 7 + package.json | 1 + services/api.service.ts | 220 +++++++++----------------------- services/qbittorrent.service.ts | 28 +++- 4 files changed, 89 insertions(+), 167 deletions(-) diff --git a/package-lock.json b/package-lock.json index cb2d742..ceaeba2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "jest": "^29.3.1", "moleculer-repl": "^0.7.3", "prettier": "^2.8.0", + "qbittorrent-api-v2": "^1.2.2", "ts-jest": "^29.0.3", "ts-node": "^10.9.1", "typescript": "^4.9.3" @@ -6285,6 +6286,12 @@ } ] }, + "node_modules/qbittorrent-api-v2": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/qbittorrent-api-v2/-/qbittorrent-api-v2-1.2.2.tgz", + "integrity": "sha512-v9nkeikj8EjZJ2Ud/QAkTr038PAM4Xk8WXtR8scVb3jidFGs8gMOrZIL05gy4JNo4b0/vxKlwcC2Zu4IpuMslw==", + "dev": true + }, "node_modules/qs": { "version": "6.11.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", diff --git a/package.json b/package.json index b86abcd..30479f9 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "jest": "^29.3.1", "moleculer-repl": "^0.7.3", "prettier": "^2.8.0", + "qbittorrent-api-v2": "^1.2.2", "ts-jest": "^29.0.3", "ts-node": "^10.9.1", "typescript": "^4.9.3" diff --git a/services/api.service.ts b/services/api.service.ts index fc864ce..ab918aa 100644 --- a/services/api.service.ts +++ b/services/api.service.ts @@ -1,168 +1,68 @@ -import type { Context, ServiceSchema } from "moleculer"; -import type { ApiSettingsSchema, GatewayResponse, IncomingRequest, Route } from "moleculer-web"; +import fs from "fs"; +import { Service, ServiceBroker } from "moleculer"; import ApiGateway from "moleculer-web"; -interface Meta { - userAgent?: string | null | undefined; - user?: object | null | undefined; -} +export default class ApiService extends Service { + public constructor(broker: ServiceBroker) { + super(broker); + this.parseServiceSchema({ + name: "api", + mixins: [ApiGateway], + // More info about settings: https://moleculer.services/docs/0.14/moleculer-web.html + settings: { + port: process.env.PORT || 3060, + routes: [ + { + path: "/api", + whitelist: ["**"], + cors: { + origin: "*", + methods: ["GET", "OPTIONS", "POST", "PUT", "DELETE"], + allowedHeaders: ["*"], + exposedHeaders: [], + credentials: false, + maxAge: 3600, + }, + use: [], + mergeParams: true, + authentication: false, + authorization: false, + autoAliases: true, + aliases: {}, + callingOptions: {}, -const ApiService: ServiceSchema = { - name: "api", - mixins: [ApiGateway], - - // More info about settings: https://moleculer.services/docs/0.14/moleculer-web.html - settings: { - // Exposed port - port: process.env.PORT != null ? Number(process.env.PORT) : 3060, - - // Exposed IP - ip: "0.0.0.0", - - // Global Express middlewares. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Middlewares - use: [], - - routes: [ - { - path: "/api", - - whitelist: ["**"], - - // Route-level Express middlewares. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Middlewares - use: [], - - // Enable/disable parameter merging method. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Disable-merging - mergeParams: true, - - // Enable authentication. Implement the logic into `authenticate` method. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Authentication - authentication: false, - - // Enable authorization. Implement the logic into `authorize` method. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Authorization - authorization: false, - - // The auto-alias feature allows you to declare your route alias directly in your services. - // The gateway will dynamically build the full routes from service schema. - autoAliases: true, - - aliases: {}, - - /** - * Before call hook. You can check the request. - * - onBeforeCall( - ctx: Context, - route: Route, - req: IncomingRequest, - res: GatewayResponse, - ): void { - // Set request headers to context meta - ctx.meta.userAgent = req.headers["user-agent"]; - }, */ - - /** - * After call hook. You can modify the data. - * - onAfterCall( - ctx: Context, - route: Route, - req: IncomingRequest, - res: GatewayResponse, - data: unknown, - ): unknown { - // Async function which return with Promise - // return this.doSomething(ctx, res, data); - return data; - }, */ - - // Calling options. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Calling-options - // callingOptions: {}, - - bodyParsers: { - json: { - strict: false, - limit: "1MB", + bodyParsers: { + json: { + strict: false, + limit: "1MB", + }, + urlencoded: { + extended: true, + limit: "1MB", + }, + }, + mappingPolicy: "all", // Available values: "all", "restrict" + logging: true, }, - urlencoded: { - extended: true, - limit: "1MB", + + { + path: "/logs", + use: [ApiGateway.serveStatic("logs")], }, + ], + log4XXResponses: false, + logRequestParams: true, + logResponseData: true, + assets: { + folder: "public", + // Options to `server-static` module + options: {}, }, - - // Mapping policy setting. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Mapping-policy - mappingPolicy: "all", // Available values: "all", "restrict" - - // Enable/disable logging - logging: true, }, - ], + events: {}, - // Do not log client side errors (does not log an error response when the error.code is 400<=X<500) - log4XXResponses: false, - // Logging the request parameters. Set to any log level to enable it. E.g. "info" - logRequestParams: null, - // Logging the response data. Set to any log level to enable it. E.g. "info" - logResponseData: null, - - // Serve assets from "public" folder. More info: https://moleculer.services/docs/0.14/moleculer-web.html#Serve-static-files - assets: { - folder: "public", - - // Options to `server-static` module - options: {}, - }, - }, - - methods: { - /** - * Authenticate the request. It check the `Authorization` token value in the request header. - * Check the token value & resolve the user by the token. - * The resolved user will be available in `ctx.meta.user` - * - * PLEASE NOTE, IT'S JUST AN EXAMPLE IMPLEMENTATION. DO NOT USE IN PRODUCTION! - */ - authenticate( - ctx: Context, - route: Route, - req: IncomingRequest, - ): Record | null { - // Read the token from header - const auth = req.headers.authorization; - - if (auth && auth.startsWith("Bearer")) { - const token = auth.slice(7); - - // Check the token. Tip: call a service which verify the token. E.g. `accounts.resolveToken` - if (token === "123456") { - // Returns the resolved user. It will be set to the `ctx.meta.user` - return { id: 1, name: "John Doe" }; - } - // Invalid token - throw new ApiGateway.Errors.UnAuthorizedError( - ApiGateway.Errors.ERR_INVALID_TOKEN, - null, - ); - } else { - // No token. Throw an error or do nothing if anonymous access is allowed. - // throw new E.UnAuthorizedError(E.ERR_NO_TOKEN); - return null; - } - }, - - /** - * Authorize the request. Check that the authenticated user has right to access the resource. - * - * PLEASE NOTE, IT'S JUST AN EXAMPLE IMPLEMENTATION. DO NOT USE IN PRODUCTION! - */ - authorize(ctx: Context, route: Route, req: IncomingRequest) { - // Get the authenticated user. - const { user } = ctx.meta; - - // It check the `auth` property in action schema. - if (req.$action.auth === "required" && !user) { - throw new ApiGateway.Errors.UnAuthorizedError("NO_RIGHTS", null); - } - }, - }, -}; - -export default ApiService; + methods: {}, + started(): any {}, + }); + } +} diff --git a/services/qbittorrent.service.ts b/services/qbittorrent.service.ts index ccc6a56..add4fb5 100644 --- a/services/qbittorrent.service.ts +++ b/services/qbittorrent.service.ts @@ -16,14 +16,28 @@ export default class QBittorrentService extends Service { actions: { connect: { rest: "POST /connect", - handler: async (ctx: Context<{}>) => { - const client = new qBittorrentClient( - "http://127.0.0.1:8080", - "admin", - "adminadmin", + handler: async ( + ctx: Context<{ + username: string; + password: string; + hostname: string; + port: string; + protocol: string; + name: string; + }>, + ) => { + const { username, password, hostname, port, protocol } = ctx.params; + this.meta = new qBittorrentClient( + `${protocol}://${hostname}:${port}`, + `${username}`, + `${password}`, ); - console.log(client); - return { foo: "bar" }; + }, + }, + getClientInfo: { + rest: "GET /getClientInfo", + handler: async (ctx: Context<{}>) => { + return await this.meta.app.buildInfo(); }, }, }, From 3676bb0699e799c02e6e372d152838bcd53283a2 Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Sat, 16 Sep 2023 15:36:43 -0400 Subject: [PATCH 03/12] =?UTF-8?q?=F0=9F=94=A7=20Added=20more=20data=20to?= =?UTF-8?q?=20the=20return=20value=20of=20getClientInfo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/qbittorrent.service.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/services/qbittorrent.service.ts b/services/qbittorrent.service.ts index add4fb5..f53473d 100644 --- a/services/qbittorrent.service.ts +++ b/services/qbittorrent.service.ts @@ -37,8 +37,14 @@ export default class QBittorrentService extends Service { getClientInfo: { rest: "GET /getClientInfo", handler: async (ctx: Context<{}>) => { - return await this.meta.app.buildInfo(); - }, + console.log(this.meta.app); + return { + buildInfo: await this.meta.app.buildInfo(), + version: await this.meta.app.version(), + webAPIVersion: await this.meta.app.webapiVersion(), + } + } + }, }, methods: {}, From 9e626720dc2c0cd1e3e3e5ebd8ea23f6000e9674 Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Mon, 18 Sep 2023 15:35:21 -0400 Subject: [PATCH 04/12] =?UTF-8?q?=F0=9F=8F=97=20Scaffold=20for=20Prowlarr?= =?UTF-8?q?=20wrapper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 79 +++++++++++++++++++++++++++++++++ package.json | 1 + services/prowlarr.service.ts | 62 ++++++++++++++++++++++++++ services/qbittorrent.service.ts | 5 +-- 4 files changed, 144 insertions(+), 3 deletions(-) create mode 100644 services/prowlarr.service.ts diff --git a/package-lock.json b/package-lock.json index ceaeba2..d2953df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "@types/node": "^18.11.9", "@typescript-eslint/eslint-plugin": "^5.44.0", "@typescript-eslint/parser": "^5.44.0", + "axios": "^1.5.0", "concurrently": "^7.6.0", "cross-env": "^7.0.3", "eslint": "^8.28.0", @@ -2038,6 +2039,12 @@ "node": ">=8" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", @@ -2050,6 +2057,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", + "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/babel-jest": { "version": "29.6.4", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.4.tgz", @@ -2570,6 +2588,18 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "9.5.0", "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", @@ -2782,6 +2812,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -3735,6 +3774,26 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -3744,6 +3803,20 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", @@ -6261,6 +6334,12 @@ "node": ">=6" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", diff --git a/package.json b/package.json index 30479f9..6fdba4b 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@types/node": "^18.11.9", "@typescript-eslint/eslint-plugin": "^5.44.0", "@typescript-eslint/parser": "^5.44.0", + "axios": "^1.5.0", "concurrently": "^7.6.0", "cross-env": "^7.0.3", "eslint": "^8.28.0", diff --git a/services/prowlarr.service.ts b/services/prowlarr.service.ts new file mode 100644 index 0000000..6a94148 --- /dev/null +++ b/services/prowlarr.service.ts @@ -0,0 +1,62 @@ +"use strict"; +import { Context, Service, ServiceBroker, ServiceSchema, Errors } from "moleculer"; +import axios from "axios"; + +export default class ProwlarrService extends Service { + // @ts-ignore + public constructor( + public broker: ServiceBroker, + schema: ServiceSchema<{}> = { name: "prowlarr" }, + ) { + super(broker); + this.parseServiceSchema({ + name: "prowlarr", + mixins: [], + hooks: {}, + actions: { + connect: { + rest: "POST /connect", + handler: async (ctx: Context<{}>) => { + const result = await axios.request({ + url: "http://192.168.1.183:9696/api", + method: "GET", + headers: { + "X-Api-Key": "163ef9a683874f65b53c7be87354b38b", + }, + }); + console.log(result.data); + }, + }, + indexers: { + rest: "GET /indexers", + handler: async (ctx: Context<{}>) => { + const result = await axios.request({ + url: "http://192.168.1.183:9696/api/v1/indexer", + method: "GET", + headers: { + "X-Api-Key": "163ef9a683874f65b53c7be87354b38b", + }, + }); + return result.data; + }, + }, + ping: { + rest: "GET /ping", + handler: async (ctx: Context<{}>) => { + const foo = await axios.request({ + url: "http://192.168.1.183:9696/ping", + method: "GET", + headers: { + Accept: "application/json", + "X-Api-Key": "163ef9a683874f65b53c7be87354b38b", + }, + }); + console.log(foo.data); + return true; + }, + }, + }, + methods: {}, + }); + } +} diff --git a/services/qbittorrent.service.ts b/services/qbittorrent.service.ts index f53473d..b829eb9 100644 --- a/services/qbittorrent.service.ts +++ b/services/qbittorrent.service.ts @@ -42,9 +42,8 @@ export default class QBittorrentService extends Service { buildInfo: await this.meta.app.buildInfo(), version: await this.meta.app.version(), webAPIVersion: await this.meta.app.webapiVersion(), - } - } - + }; + }, }, }, methods: {}, From df07dff1db95e4f6918a97ac0556825230629c8b Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Mon, 12 Feb 2024 23:07:14 -0500 Subject: [PATCH 05/12] =?UTF-8?q?=F0=9F=A7=B2=20Added=20Prowlarr=20search?= =?UTF-8?q?=20endpoint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/prowlarr.service.ts | 76 ++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 7 deletions(-) diff --git a/services/prowlarr.service.ts b/services/prowlarr.service.ts index 6a94148..f030b86 100644 --- a/services/prowlarr.service.ts +++ b/services/prowlarr.service.ts @@ -16,30 +16,92 @@ export default class ProwlarrService extends Service { actions: { connect: { rest: "POST /connect", - handler: async (ctx: Context<{}>) => { + handler: async ( + ctx: Context<{ + host: string; + port: string; + apiKey: string; + }>, + ) => { + const { host, port, apiKey } = ctx.params; const result = await axios.request({ - url: "http://192.168.1.183:9696/api", + url: `http://${host}:${port}/api`, method: "GET", headers: { - "X-Api-Key": "163ef9a683874f65b53c7be87354b38b", + "X-Api-Key": apiKey, }, }); console.log(result.data); }, }, - indexers: { + getIndexers: { rest: "GET /indexers", - handler: async (ctx: Context<{}>) => { + handler: async ( + ctx: Context<{ host: string; port: string; apiKey: string }>, + ) => { + const { host, port, apiKey } = ctx.params; const result = await axios.request({ - url: "http://192.168.1.183:9696/api/v1/indexer", + url: `http://${host}:${port}/api/v1/indexer`, method: "GET", headers: { - "X-Api-Key": "163ef9a683874f65b53c7be87354b38b", + "X-Api-Key": apiKey, }, }); return result.data; }, }, + search: { + rest: "GET /search", + handler: async ( + ctx: Context<{ + host: string; + port: string; + apiKey: string; + query: string; + type: string; + indexerIds: string; + categories: string; + limit: number; + offset: number; + }>, + ) => { + const { + indexerIds, + categories, + host, + port, + apiKey, + query, + type, + limit, + offset, + } = ctx.params; + + const indexers = indexerIds.split(",").map((index) => parseInt(index, 10)); + const searchCategories = categories + .split(",") + .map((category) => parseInt(category, 10)); + + const result = await axios({ + url: `http://${host}:${port}/api/v1/search`, + method: "GET", + params: { + query, + type, + indexers, + searchCategories, + limit, + offset, + }, + headers: { + Accept: "application/json", + "X-Api-Key": `${apiKey}`, + }, + }); + console.log(result); + return result.data; + }, + }, ping: { rest: "GET /ping", handler: async (ctx: Context<{}>) => { From 54974db2de445692edc739ef9e859fe3309a49fc Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Thu, 29 Feb 2024 08:37:24 -0600 Subject: [PATCH 06/12] =?UTF-8?q?=F0=9F=A7=B2=20Added=20parse-torrent=20fo?= =?UTF-8?q?r=20metadata?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + package-lock.json | 391 +++++++++++++++++++++++++------- package.json | 4 +- services/prowlarr.service.ts | 14 +- services/qbittorrent.service.ts | 43 ++++ 5 files changed, 366 insertions(+), 88 deletions(-) diff --git a/.gitignore b/.gitignore index 47ea0ab..825a56d 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,5 @@ jspm_packages/ # Don't track transpiled files dist/ +.DS_Store +*.torrent diff --git a/package-lock.json b/package-lock.json index d2953df..918ddcf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,12 +11,14 @@ "@robertklep/qbittorrent": "^1.0.1", "ioredis": "^5.0.0", "moleculer": "^0.14.27", - "moleculer-web": "^0.10.5" + "moleculer-web": "^0.10.5", + "parse-torrent": "^9.1.5" }, "devDependencies": { "@jest/globals": "^29.3.1", "@types/jest": "^29.2.3", "@types/node": "^18.11.9", + "@types/parse-torrent": "^5.8.7", "@typescript-eslint/eslint-plugin": "^5.44.0", "@typescript-eslint/parser": "^5.44.0", "axios": "^1.5.0", @@ -63,12 +65,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.13", + "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" }, "engines": { @@ -201,12 +203,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", - "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dev": true, "dependencies": { - "@babel/types": "^7.22.10", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -241,22 +243,22 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" @@ -339,18 +341,18 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "dev": true, "engines": { "node": ">=6.9.0" @@ -380,12 +382,12 @@ } }, "node_modules/@babel/highlight": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz", - "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, @@ -465,9 +467,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.14", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.14.tgz", - "integrity": "sha512-1KucTHgOvaw/LzCVrEOAyXkr9rQlp0A1HiHRYnSUE9dmb8PvPW7o5sscg+5169r54n3vGlbx6GevTE/Iw/P3AQ==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", + "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -666,34 +668,34 @@ } }, "node_modules/@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.11.tgz", - "integrity": "sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.0.tgz", + "integrity": "sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.10", - "@babel/generator": "^7.22.10", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.11", - "@babel/types": "^7.22.11", - "debug": "^4.1.0", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -710,13 +712,13 @@ } }, "node_modules/@babel/types": { - "version": "7.22.11", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz", - "integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -1477,12 +1479,41 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/magnet-uri": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@types/magnet-uri/-/magnet-uri-5.1.5.tgz", + "integrity": "sha512-SbBjlb1KGe38VfjRR+mwqztJd/4skhdKkRbIzPDhTy7IAeEAPZWIVSEkZw00Qr4ZZOGR3/ATJ20WWPBfrKHGdA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "18.17.12", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.12.tgz", "integrity": "sha512-d6xjC9fJ/nSnfDeU0AMDsaJyb1iHsqCSOdi84w4u+SlN/UgQdY5tRhpMzaFYsI4mnpvgTivEaQd0yOUhAtOnEQ==", "dev": true }, + "node_modules/@types/parse-torrent": { + "version": "5.8.7", + "resolved": "https://registry.npmjs.org/@types/parse-torrent/-/parse-torrent-5.8.7.tgz", + "integrity": "sha512-vZtYe450hO+KL7B5fejM8CHWg1LPZKeVXlolphPsWf6n4H0ZUlI6ICbqHoaFmH7JQmU2yRbGgyvqqizdFuGPFQ==", + "dev": true, + "dependencies": { + "@types/magnet-uri": "*", + "@types/node": "*", + "@types/parse-torrent-file": "*" + } + }, + "node_modules/@types/parse-torrent-file": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/parse-torrent-file/-/parse-torrent-file-4.0.6.tgz", + "integrity": "sha512-SxqVth0Iv0WuEkqWS5MaY4S4Tlyi+QHkElQREvsUPw2xHcPgKyQ2dkJRRv5vAxmLzH+tnMdOj1Nws/wsenbzUw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/semver": { "version": "7.5.1", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.1.tgz", @@ -2058,12 +2089,12 @@ } }, "node_modules/axios": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz", - "integrity": "sha512-D4DdjDo5CY50Qms0qGQTTw6Q44jl7zRwY7bthds06pUGfChBCTcQs+N743eFWGEd6pRTMd6A+I87aWyFV5wiZQ==", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", + "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", "dev": true, "dependencies": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.4", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -2209,6 +2240,16 @@ } ] }, + "node_modules/bencode": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/bencode/-/bencode-2.0.3.tgz", + "integrity": "sha512-D/vrAD4dLVX23NalHwb8dSvsUsxeRPO8Y7ToKA015JQYq69MLDOMkC0uGZYA/MPpltLO8rt8eqFC2j8DxjTZ/w==" + }, + "node_modules/bep53-range": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bep53-range/-/bep53-range-1.1.1.tgz", + "integrity": "sha512-ct6s33iiwRCUPp9KXnJ4QMWDgHIgaw36caK/5XEQ9L8dCzSQlJt1Vk6VmHh1VD4AlGCAI4C2zmtfItifBBPrhQ==" + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", @@ -2220,6 +2261,25 @@ "readable-stream": "^3.4.0" } }, + "node_modules/blob-to-buffer": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/blob-to-buffer/-/blob-to-buffer-1.2.9.tgz", + "integrity": "sha512-BF033y5fN6OCofD3vgHmNtwZWRcq9NLyyxyILx9hfMy1sXYy4ojFl765hJ2lP0YaN2fuxPaLO2Vzzoxy0FLFFA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/body-parser": { "version": "1.20.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", @@ -2755,6 +2815,20 @@ } } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dedent": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", @@ -3037,14 +3111,15 @@ } }, "node_modules/es5-ext": { - "version": "0.10.62", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", - "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", "dev": true, "hasInstallScript": true, "dependencies": { "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", "next-tick": "^1.1.0" }, "engines": { @@ -3459,6 +3534,37 @@ "node": ">=4.0" } }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dev": true, + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esniff/node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/esniff/node_modules/d/node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", @@ -3775,9 +3881,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", "dev": true, "funding": [ { @@ -3917,6 +4023,17 @@ "node": ">=8.0.0" } }, + "node_modules/get-stdin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -5422,6 +5539,29 @@ "es5-ext": "~0.10.2" } }, + "node_modules/magnet-uri": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/magnet-uri/-/magnet-uri-6.2.0.tgz", + "integrity": "sha512-O9AgdDwT771fnUj0giPYu/rACpz8173y8UXCSOdLITjOVfBenZ9H9q3FqQmveK+ORUMuD+BkKNSZP8C3+IMAKQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "bep53-range": "^1.1.0", + "thirty-two": "^1.0.2" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -5548,6 +5688,17 @@ "node": ">=6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5775,11 +5926,10 @@ "dev": true }, "node_modules/needle": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz", - "integrity": "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", "dependencies": { - "debug": "^3.2.6", "iconv-lite": "^0.6.3", "sax": "^1.2.4" }, @@ -5790,14 +5940,6 @@ "node": ">= 4.4.x" } }, - "node_modules/needle/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" - } - }, "node_modules/needle/node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -6105,6 +6247,37 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-torrent": { + "version": "9.1.5", + "resolved": "https://registry.npmjs.org/parse-torrent/-/parse-torrent-9.1.5.tgz", + "integrity": "sha512-K8FXRwTOaZMI0/xuv0dpng1MVHZRtMJ0jRWBJ3qZWVNTrC1MzWUxm9QwaXDz/2qPhV2XC4UIHI92IGHwseAwaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "bencode": "^2.0.2", + "blob-to-buffer": "^1.2.9", + "get-stdin": "^8.0.0", + "magnet-uri": "^6.2.0", + "queue-microtask": "^1.2.3", + "simple-get": "^4.0.1", + "simple-sha1": "^3.1.0" + }, + "bin": { + "parse-torrent": "bin/cmd.js" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -6389,7 +6562,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -6635,6 +6807,11 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rusha": { + "version": "0.8.14", + "resolved": "https://registry.npmjs.org/rusha/-/rusha-0.8.14.tgz", + "integrity": "sha512-cLgakCUf6PedEu15t8kbsjnwIFFR2D4RfL+W3iWFJ4iac7z4B0ZI8fxy4R3J956kAI68HclCFGL8MPoUVC3qVA==" + }, "node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", @@ -6702,9 +6879,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" }, "node_modules/semver": { "version": "7.5.4", @@ -6848,6 +7025,58 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-sha1": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/simple-sha1/-/simple-sha1-3.1.0.tgz", + "integrity": "sha512-ArTptMRC1v08H8ihPD6l0wesKvMfF9e8XL5rIHPanI7kGOsSsbY514MwVu6X1PITHCTB2F08zB7cyEbfc4wQjg==", + "dependencies": { + "queue-microtask": "^1.2.2", + "rusha": "^0.8.13" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -7164,6 +7393,14 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/thirty-two": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/thirty-two/-/thirty-two-1.0.2.tgz", + "integrity": "sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA==", + "engines": { + "node": ">=0.2.6" + } + }, "node_modules/timers-ext": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", diff --git a/package.json b/package.json index 6fdba4b..e341c24 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@jest/globals": "^29.3.1", "@types/jest": "^29.2.3", "@types/node": "^18.11.9", + "@types/parse-torrent": "^5.8.7", "@typescript-eslint/eslint-plugin": "^5.44.0", "@typescript-eslint/parser": "^5.44.0", "axios": "^1.5.0", @@ -47,7 +48,8 @@ "@robertklep/qbittorrent": "^1.0.1", "ioredis": "^5.0.0", "moleculer": "^0.14.27", - "moleculer-web": "^0.10.5" + "moleculer-web": "^0.10.5", + "parse-torrent": "^9.1.5" }, "engines": { "node": ">= 16.x.x" diff --git a/services/prowlarr.service.ts b/services/prowlarr.service.ts index f030b86..b89359c 100644 --- a/services/prowlarr.service.ts +++ b/services/prowlarr.service.ts @@ -59,8 +59,8 @@ export default class ProwlarrService extends Service { apiKey: string; query: string; type: string; - indexerIds: string; - categories: string; + indexerIds: [number]; + categories: [number]; limit: number; offset: number; }>, @@ -77,19 +77,14 @@ export default class ProwlarrService extends Service { offset, } = ctx.params; - const indexers = indexerIds.split(",").map((index) => parseInt(index, 10)); - const searchCategories = categories - .split(",") - .map((category) => parseInt(category, 10)); - const result = await axios({ url: `http://${host}:${port}/api/v1/search`, method: "GET", params: { query, type, - indexers, - searchCategories, + indexerIds, + categories, limit, offset, }, @@ -98,7 +93,6 @@ export default class ProwlarrService extends Service { "X-Api-Key": `${apiKey}`, }, }); - console.log(result); return result.data; }, }, diff --git a/services/qbittorrent.service.ts b/services/qbittorrent.service.ts index b829eb9..89752a8 100644 --- a/services/qbittorrent.service.ts +++ b/services/qbittorrent.service.ts @@ -1,6 +1,8 @@ "use strict"; import { Context, Service, ServiceBroker, ServiceSchema, Errors } from "moleculer"; import { qBittorrentClient } from "@robertklep/qbittorrent"; +import parseTorrent from "parse-torrent"; +import { readFileSync, writeFileSync } from "fs"; export default class QBittorrentService extends Service { // @ts-ignore @@ -32,6 +34,7 @@ export default class QBittorrentService extends Service { `${username}`, `${password}`, ); + console.log(this.meta); }, }, getClientInfo: { @@ -45,6 +48,46 @@ export default class QBittorrentService extends Service { }; }, }, + addTorrent: { + rest: "POST /addTorrent", + handler: async ( + ctx: Context<{ + torrentToDownload: any; + comicObjectId: string; + }>, + ) => { + try { + const { torrentToDownload } = ctx.params; + console.log(torrentToDownload); + const response = await fetch(torrentToDownload, { + method: "GET", + }); + const buffer = await response.arrayBuffer(); + writeFileSync(`mithrandir.torrent`, Buffer.from(buffer)); + + const result = await this.meta.torrents.add({ + torrents: { + buffer: readFileSync("mithrandir.torrent"), + }, + // start this torrent in a paused state (see Torrent type for options) + paused: true, + }); + + return { + result, + metadata: parseTorrent(readFileSync("mithrandir.torrent")), + }; + } catch (err) { + console.error(err); + } + }, + }, + getTorrents: { + rest: "POST /getTorrents", + handler: async (ctx: Context<{}>) => { + return await this.meta.torrents.info(); + }, + }, }, methods: {}, }); From 7ac5cdb777822f293db0d7266bc75b912ad38ffa Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Sun, 3 Mar 2024 12:20:01 -0500 Subject: [PATCH 07/12] =?UTF-8?q?=F0=9F=8F=97=EF=B8=8F=20Adding=20comments?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/qbittorrent.service.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/services/qbittorrent.service.ts b/services/qbittorrent.service.ts index 89752a8..3790f2c 100644 --- a/services/qbittorrent.service.ts +++ b/services/qbittorrent.service.ts @@ -57,14 +57,15 @@ export default class QBittorrentService extends Service { }>, ) => { try { - const { torrentToDownload } = ctx.params; + const { torrentToDownload, comicObjectId } = ctx.params; console.log(torrentToDownload); const response = await fetch(torrentToDownload, { method: "GET", }); + // Read the buffer to a file const buffer = await response.arrayBuffer(); writeFileSync(`mithrandir.torrent`, Buffer.from(buffer)); - + // Add the torrent to qbittorrent's queue, paused. const result = await this.meta.torrents.add({ torrents: { buffer: readFileSync("mithrandir.torrent"), @@ -72,10 +73,18 @@ export default class QBittorrentService extends Service { // start this torrent in a paused state (see Torrent type for options) paused: true, }); - + const { name, infoHash, announce } = parseTorrent( + readFileSync("mithrandir.torrent"), + ); + await this.broker.call("library.applyTorrentDownloadMetadata", { + name, + torrentToDownload, + comicObjectId, + announce, + infoHash, + }); return { result, - metadata: parseTorrent(readFileSync("mithrandir.torrent")), }; } catch (err) { console.error(err); From daca4e63938a9f9d066a3c083902dc897e3d5929 Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Thu, 7 Mar 2024 05:51:13 -0600 Subject: [PATCH 08/12] =?UTF-8?q?=F0=9F=A7=B2=20Added=20a=20torrent=20deta?= =?UTF-8?q?il=20endpoint?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/qbittorrent.service.ts | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/services/qbittorrent.service.ts b/services/qbittorrent.service.ts index 3790f2c..9f23875 100644 --- a/services/qbittorrent.service.ts +++ b/services/qbittorrent.service.ts @@ -1,15 +1,12 @@ -"use strict"; -import { Context, Service, ServiceBroker, ServiceSchema, Errors } from "moleculer"; -import { qBittorrentClient } from "@robertklep/qbittorrent"; -import parseTorrent from "parse-torrent"; import { readFileSync, writeFileSync } from "fs"; +import { qBittorrentClient } from "@robertklep/qbittorrent"; +import type { Context, ServiceBroker, ServiceSchema } from "moleculer"; +import { Errors, Service } from "moleculer"; +import parseTorrent from "parse-torrent"; export default class QBittorrentService extends Service { // @ts-ignore - public constructor( - public broker: ServiceBroker, - schema: ServiceSchema<{}> = { name: "qbittorrent" }, - ) { + constructor(public broker: ServiceBroker, schema: ServiceSchema<{}> = { name: "qbittorrent" }) { super(broker); this.parseServiceSchema({ name: "qbittorrent", @@ -93,8 +90,16 @@ export default class QBittorrentService extends Service { }, getTorrents: { rest: "POST /getTorrents", - handler: async (ctx: Context<{}>) => { - return await this.meta.torrents.info(); + handler: async (ctx: Context<{}>) => await this.meta.torrents.info(), + }, + getTorrentDetails: { + rest: "POST /getTorrentDetails", + handler: async (ctx: Context<{ infoHashes: [string] }>) => { + const infoHashes = Object.values(ctx.params); + const torrentDetails = infoHashes.map(async (infoHash) => { + return await this.meta.torrents.properties(infoHash); + }); + return Promise.all(torrentDetails); }, }, }, From f476e847cea6805517d83c05c9991d8cbf44b8a3 Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Tue, 12 Mar 2024 10:15:10 -0400 Subject: [PATCH 09/12] =?UTF-8?q?=F0=9F=A7=B2=20Added=20couple=20of=20torr?= =?UTF-8?q?ent-related=20endpoints?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/qbittorrent.service.ts | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/services/qbittorrent.service.ts b/services/qbittorrent.service.ts index 9f23875..84974ac 100644 --- a/services/qbittorrent.service.ts +++ b/services/qbittorrent.service.ts @@ -12,6 +12,7 @@ export default class QBittorrentService extends Service { name: "qbittorrent", mixins: [], hooks: {}, + settings: {}, actions: { connect: { rest: "POST /connect", @@ -26,12 +27,16 @@ export default class QBittorrentService extends Service { }>, ) => { const { username, password, hostname, port, protocol } = ctx.params; + this.meta = new qBittorrentClient( `${protocol}://${hostname}:${port}`, `${username}`, `${password}`, ); console.log(this.meta); + if (this.meta) { + return { success: true, message: "Logged in successfully" }; + } }, }, getClientInfo: { @@ -102,8 +107,28 @@ export default class QBittorrentService extends Service { return Promise.all(torrentDetails); }, }, + checkForDeletedTorrents: { + rest: "GET /checkForDeletedTorrents", + handler: async (ctx: Context<{ infoHashes: [string] }>) => { + await this.broker.call("qbittorrent.connect", { + hostname: "localhost", + protocol: "http", + port: "8080", + username: "admin", + password: "password", + }); + const torrents: any = await this.broker.call("qbittorrent.getTorrents", {}); + const deletedTorrents = this.detectDeletedTorrents( + torrents.map((torrent: any) => torrent.hash), + ); + return deletedTorrents; + }, + }, }, - methods: {}, + methods: { + detectDeletedTorrents(currentHashes) {}, + }, + async started() {}, }); } } From 20fe2327b4cf1c1dbbce8dda5c52ec118687fca9 Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Sun, 17 Mar 2024 15:04:50 -0400 Subject: [PATCH 10/12] =?UTF-8?q?=F0=9F=A7=B2=20Implemented=20a=20login=20?= =?UTF-8?q?method?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/qbittorrent.service.ts | 58 ++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/services/qbittorrent.service.ts b/services/qbittorrent.service.ts index 84974ac..001aa19 100644 --- a/services/qbittorrent.service.ts +++ b/services/qbittorrent.service.ts @@ -14,6 +14,14 @@ export default class QBittorrentService extends Service { hooks: {}, settings: {}, actions: { + fetchQbittorrentCredentials: { + rest: "GET /fetchQbittorrentCredentials", + handler: async (ctx: Context<{}>) => { + return await this.broker.call("settings.getSettings", { + settingsKey: "bittorrent", + }); + }, + }, connect: { rest: "POST /connect", handler: async ( @@ -23,7 +31,7 @@ export default class QBittorrentService extends Service { hostname: string; port: string; protocol: string; - name: string; + name?: string; }>, ) => { const { username, password, hostname, port, protocol } = ctx.params; @@ -39,10 +47,43 @@ export default class QBittorrentService extends Service { } }, }, + loginWithStoredCredentials: { + rest: "POST /loginWithStoredCredentials", + handler: async (ctx: Context<{}>) => { + try { + const result: any = await this.broker.call( + "qbittorrent.fetchQbittorrentCredentials", + {}, + ); + if (result !== undefined) { + const { + client: { + host: { username, password, hostname, port, protocol }, + }, + } = result; + return await this.broker.call("qbittorrent.connect", { + username, + password, + hostname, + port, + protocol, + }); + } + } catch (err) { + return { + error: err, + message: + "Qbittorrent credentials not found, please configure them in Settings.", + }; + } + }, + }, + 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(), version: await this.meta.app.version(), @@ -59,6 +100,7 @@ export default class QBittorrentService extends Service { }>, ) => { try { + await this.broker.call("qbittorrent.loginWithStoredCredentials", {}); const { torrentToDownload, comicObjectId } = ctx.params; console.log(torrentToDownload); const response = await fetch(torrentToDownload, { @@ -95,11 +137,15 @@ export default class QBittorrentService extends Service { }, getTorrents: { rest: "POST /getTorrents", - handler: async (ctx: Context<{}>) => await this.meta.torrents.info(), + handler: async (ctx: Context<{}>) => { + await this.broker.call("qbittorrent.loginWithStoredCredentials", {}); + return await this.meta.torrents.info(); + }, }, getTorrentDetails: { rest: "POST /getTorrentDetails", handler: async (ctx: Context<{ infoHashes: [string] }>) => { + await this.broker.call("qbittorrent.loginWithStoredCredentials", {}); const infoHashes = Object.values(ctx.params); const torrentDetails = infoHashes.map(async (infoHash) => { return await this.meta.torrents.properties(infoHash); @@ -110,13 +156,7 @@ export default class QBittorrentService extends Service { checkForDeletedTorrents: { rest: "GET /checkForDeletedTorrents", handler: async (ctx: Context<{ infoHashes: [string] }>) => { - await this.broker.call("qbittorrent.connect", { - hostname: "localhost", - protocol: "http", - port: "8080", - username: "admin", - password: "password", - }); + await this.broker.call("qbittorrent.loginWithStoredCredentials", {}); const torrents: any = await this.broker.call("qbittorrent.getTorrents", {}); const deletedTorrents = this.detectDeletedTorrents( torrents.map((torrent: any) => torrent.hash), From 0eb55935fe974ba916599e171a46f24c8530e61e Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Sun, 24 Mar 2024 17:31:12 -0400 Subject: [PATCH 11/12] =?UTF-8?q?=F0=9F=A7=B2=20Added=20maindata=20realtim?= =?UTF-8?q?e=20stats?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/qbittorrent.service.ts | 53 ++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/services/qbittorrent.service.ts b/services/qbittorrent.service.ts index 001aa19..d227b87 100644 --- a/services/qbittorrent.service.ts +++ b/services/qbittorrent.service.ts @@ -61,13 +61,17 @@ export default class QBittorrentService extends Service { host: { username, password, hostname, port, protocol }, }, } = result; - return await this.broker.call("qbittorrent.connect", { + + const connection = await this.broker.call("qbittorrent.connect", { username, password, hostname, port, protocol, }); + console.log("qbittorrent connection details:"); + console.log(JSON.stringify(connection, null, 4)); + return connection; } } catch (err) { return { @@ -142,32 +146,39 @@ export default class QBittorrentService extends Service { return await this.meta.torrents.info(); }, }, - getTorrentDetails: { - rest: "POST /getTorrentDetails", - handler: async (ctx: Context<{ infoHashes: [string] }>) => { - await this.broker.call("qbittorrent.loginWithStoredCredentials", {}); - const infoHashes = Object.values(ctx.params); - const torrentDetails = infoHashes.map(async (infoHash) => { - return await this.meta.torrents.properties(infoHash); - }); - return Promise.all(torrentDetails); + getTorrentProperties: { + rest: "POST /getTorrentProperties", + handler: async (ctx: Context<{ infoHashes: string[] }>) => { + try { + const { infoHashes } = ctx.params; + await this.broker.call("qbittorrent.loginWithStoredCredentials", {}); + return await this.meta.torrents.info({ + hashes: infoHashes, + }); + } catch (err) { + console.error("An error occurred:", err); + // Consider handling the error more gracefully here, possibly returning an error response + throw err; // or return a specific error object/message + } }, }, - checkForDeletedTorrents: { - rest: "GET /checkForDeletedTorrents", - handler: async (ctx: Context<{ infoHashes: [string] }>) => { + getTorrentRealTimeStats: { + rest: "POST /getTorrentRealTimeStats", + handler: async ( + ctx: Context<{ infoHashes: { _id: string; infoHashes: string[] }[] }>, + ) => { await this.broker.call("qbittorrent.loginWithStoredCredentials", {}); - const torrents: any = await this.broker.call("qbittorrent.getTorrents", {}); - const deletedTorrents = this.detectDeletedTorrents( - torrents.map((torrent: any) => torrent.hash), - ); - return deletedTorrents; + + try { + return await this.meta.sync.maindata(1); + } catch (err) { + this.logger.error(err); + throw err; + } }, }, }, - methods: { - detectDeletedTorrents(currentHashes) {}, - }, + methods: {}, async started() {}, }); } From 63b5a19f024d2ca4aba3d0919cb4f85db1b0c0cb Mon Sep 17 00:00:00 2001 From: Rishi Ghan Date: Wed, 27 Mar 2024 22:22:57 -0500 Subject: [PATCH 12/12] =?UTF-8?q?=F0=9F=A7=B2=20Mild=20refactor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/qbittorrent.service.ts | 43 +++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/services/qbittorrent.service.ts b/services/qbittorrent.service.ts index d227b87..aa6b0d7 100644 --- a/services/qbittorrent.service.ts +++ b/services/qbittorrent.service.ts @@ -167,10 +167,45 @@ export default class QBittorrentService extends Service { handler: async ( ctx: Context<{ infoHashes: { _id: string; infoHashes: string[] }[] }>, ) => { + const { infoHashes } = ctx.params; await this.broker.call("qbittorrent.loginWithStoredCredentials", {}); try { - return await this.meta.sync.maindata(1); + // Increment rid for each call + this.rid = typeof this.rid === "number" ? this.rid + 1 : 0; + const data = await this.meta.sync.maindata(this.rid); + const torrentDetails: any = []; + + infoHashes.forEach(({ _id, infoHashes }) => { + // Initialize an object to hold details for this _id + const details: any = []; + + infoHashes.forEach((hash) => { + // Assuming 'data.torrents[hash]' retrieves the details for the hash + const torrent = data.torrents[hash]; + if (torrent) { + details.push({ + torrent, + }); + } + }); + + // If you have details for this _id, add them to the main array + if (details.length > 0) { + torrentDetails.push({ + _id, + details, + }); + } + }); + // Update rid with the latest value if needed based on the response + // Assuming `data.rid` contains the latest rid from the server + if (data.rid !== undefined) { + this.rid = data.rid; + console.log(`rid is ${this.rid}`); + } + console.log(JSON.stringify(torrentDetails, null, 4)); + return torrentDetails; } catch (err) { this.logger.error(err); throw err; @@ -179,7 +214,11 @@ export default class QBittorrentService extends Service { }, }, methods: {}, - async started() {}, + async started() { + console.log(`Initializing rid...`); + this.rid = 0; + console.log(`rid is ${this.rid}`); + }, }); } }