From 201d89e2c4cf1a070e41fba5e03580470b0238e1 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Wed, 25 Dec 2024 21:39:59 +0000 Subject: [PATCH 01/20] feat: adding torbox integration --- src/main/main.ts | 3 +++ .../services/download/download-manager.ts | 14 ++++++++++ src/main/services/download/torbox.ts | 23 ++++++++-------- src/renderer/src/assets/icons/torbox.webp | Bin 0 -> 12150 bytes src/renderer/src/constants.ts | 1 + .../src/pages/downloads/download-group.tsx | 25 +++++++++++++++++- .../modals/download-settings-modal.tsx | 8 +++--- src/shared/constants.ts | 1 + src/shared/index.ts | 2 +- 9 files changed, 58 insertions(+), 19 deletions(-) create mode 100644 src/renderer/src/assets/icons/torbox.webp diff --git a/src/main/main.ts b/src/main/main.ts index add619e18..81916174e 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -11,6 +11,7 @@ import { uploadGamesBatch } from "./services/library-sync"; import { Aria2 } from "./services/aria2"; import { Downloader } from "@shared"; import { IsNull, Not } from "typeorm"; +import { TorBoxClient } from "./services/download/torbox"; const loadState = async (userPreferences: UserPreferences | null) => { import("./events"); @@ -21,6 +22,8 @@ const loadState = async (userPreferences: UserPreferences | null) => { RealDebridClient.authorize(userPreferences?.realDebridApiToken); } + TorBoxClient.authorize("7371d5ec-52fa-4b87-9052-0c8c96d947cc"); + Ludusavi.addManifestToLudusaviConfig(); HydraApi.setupApi().then(() => { diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts index 80a3f6fb8..1c91c4dce 100644 --- a/src/main/services/download/download-manager.ts +++ b/src/main/services/download/download-manager.ts @@ -20,6 +20,7 @@ import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity import { RealDebridClient } from "./real-debrid"; import path from "path"; import { logger } from "../logger"; +import { TorBoxClient } from "./torbox"; export class DownloadManager { private static downloadingGameId: number | null = null; @@ -29,6 +30,7 @@ export class DownloadManager { game?.status === "active" ? await this.getDownloadPayload(game).catch(() => undefined) : undefined, + initialSeeding?.map((game) => ({ game_id: game.id, url: game.uri!, @@ -294,6 +296,18 @@ export class DownloadManager { save_path: game.downloadPath!, }; } + case Downloader.TorBox: { + const downloadUrl = await TorBoxClient.getDownloadUrl(game.uri!); + console.log(downloadUrl); + + if (!downloadUrl) return; + return { + action: "start", + game_id: game.id, + url: downloadUrl, + save_path: game.downloadPath!, + }; + } } } diff --git a/src/main/services/download/torbox.ts b/src/main/services/download/torbox.ts index 3eade81df..1ef57768a 100644 --- a/src/main/services/download/torbox.ts +++ b/src/main/services/download/torbox.ts @@ -20,7 +20,7 @@ export class TorBoxClient { Authorization: `Bearer ${apiToken}`, }, }); - this.apiToken = apiToken; + this.apiToken = "7371d5ec-52fa-4b87-9052-0c8c96d947cc"; } static async addMagnet(magnet: string) { @@ -55,22 +55,16 @@ export class TorBoxClient { } static async requestLink(id: number) { - const searchParams = new URLSearchParams({}); - - searchParams.set("token", this.apiToken); - searchParams.set("torrent_id", id.toString()); - searchParams.set("zip_link", "true"); + const searchParams = new URLSearchParams({ + token: this.apiToken, + torrent_id: id.toString(), + zip_link: "true", + }); const response = await this.instance.get( "/torrents/requestdl?" + searchParams.toString() ); - if (response.status !== 200) { - logger.error(response.data.error); - logger.error(response.data.detail); - return null; - } - return response.data.data; } @@ -94,4 +88,9 @@ export class TorBoxClient { const torrent = await this.addMagnet(magnetUri); return torrent.torrent_id; } + + static async getDownloadUrl(uri: string) { + const id = await this.getTorrentId(uri); + return this.requestLink(id); + } } diff --git a/src/renderer/src/assets/icons/torbox.webp b/src/renderer/src/assets/icons/torbox.webp new file mode 100644 index 0000000000000000000000000000000000000000..68d68531b4ebc3e12028bedd4d3946060d4196d0 GIT binary patch literal 12150 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
- {DOWNLOADER_NAME[game.downloader]} + {game.downloader === Downloader.TorBox ? ( +
+ TorBox + TorBox +
+ ) : ( + {DOWNLOADER_NAME[game.downloader]} + )}
diff --git a/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx b/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx index 191d9ac16..8d650c170 100644 --- a/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx @@ -68,11 +68,9 @@ export function DownloadSettingsModal({ return true; }); - /* Gives preference to Real Debrid */ - const selectedDownloader = filteredDownloaders.includes( - Downloader.RealDebrid - ) - ? Downloader.RealDebrid + /* Gives preference to TorBox */ + const selectedDownloader = filteredDownloaders.includes(Downloader.TorBox) + ? Downloader.TorBox : filteredDownloaders[0]; setSelectedDownloader( diff --git a/src/shared/constants.ts b/src/shared/constants.ts index 2d313abb9..4ab7443d0 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -4,6 +4,7 @@ export enum Downloader { Gofile, PixelDrain, Qiwi, + TorBox, } export enum DownloadSourceStatus { diff --git a/src/shared/index.ts b/src/shared/index.ts index 858683919..e0b09deb0 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -92,7 +92,7 @@ export const getDownloadersForUri = (uri: string) => { return [Downloader.RealDebrid]; if (uri.startsWith("magnet:")) { - return [Downloader.Torrent, Downloader.RealDebrid]; + return [Downloader.Torrent, Downloader.TorBox, Downloader.RealDebrid]; } return []; From 91b1c349e797ffe154eab8838129293894d9b9c6 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Wed, 25 Dec 2024 21:59:48 +0000 Subject: [PATCH 02/20] feat: adding torbox integration --- src/main/services/download/torbox.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/services/download/torbox.ts b/src/main/services/download/torbox.ts index 1ef57768a..0c0c0574f 100644 --- a/src/main/services/download/torbox.ts +++ b/src/main/services/download/torbox.ts @@ -6,7 +6,6 @@ import type { TorBoxAddTorrentRequest, TorBoxRequestLinkRequest, } from "@types"; -import { logger } from "../logger"; export class TorBoxClient { private static instance: AxiosInstance; From c9ae543d3ed2a5581ea00f2256406167cb1f0f11 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Thu, 26 Dec 2024 00:41:57 +0000 Subject: [PATCH 03/20] feat: adding automatic backup on game close --- src/main/events/cloud-save/upload-save-game.ts | 12 ++++++++++-- src/main/services/process-watcher.ts | 5 +++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/events/cloud-save/upload-save-game.ts b/src/main/events/cloud-save/upload-save-game.ts index b3a514f5c..d39ad1773 100644 --- a/src/main/events/cloud-save/upload-save-game.ts +++ b/src/main/events/cloud-save/upload-save-game.ts @@ -40,8 +40,7 @@ const bundleBackup = async ( return tarLocation; }; -const uploadSaveGame = async ( - _event: Electron.IpcMainInvokeEvent, +export const createBackup = async ( objectId: string, shop: GameShop, downloadOptionTitle: string | null @@ -108,4 +107,13 @@ const uploadSaveGame = async ( }); }; +const uploadSaveGame = async ( + _event: Electron.IpcMainInvokeEvent, + objectId: string, + shop: GameShop, + downloadOptionTitle: string | null +) => { + return createBackup(objectId, shop, downloadOptionTitle); +}; + registerEvent("uploadSaveGame", uploadSaveGame); diff --git a/src/main/services/process-watcher.ts b/src/main/services/process-watcher.ts index c6cb7e102..4aa00bb4c 100644 --- a/src/main/services/process-watcher.ts +++ b/src/main/services/process-watcher.ts @@ -7,6 +7,7 @@ import { Game } from "@main/entity"; import axios from "axios"; import { exec } from "child_process"; import { ProcessPayload } from "./download/types"; +import { createBackup } from "@main/events/cloud-save/upload-save-game"; const commands = { findWineDir: `lsof -c wine 2>/dev/null | grep '/drive_c/windows$' | head -n 1 | awk '{for(i=9;i<=NF;i++) printf "%s ", $i; print ""}'`, @@ -269,6 +270,10 @@ const onCloseGame = (game: Game) => { gamesPlaytime.delete(game.id); if (game.remoteId) { + // create backup + // todo: check for hydra cloud? + createBackup(game.objectID, game.shop, ""); + updateGamePlaytime( game, performance.now() - gamePlaytime.lastSyncTick, From 6ea1f9034b6c67c6da9cd6c8a0849f2e6902befc Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 25 Dec 2024 23:15:58 -0300 Subject: [PATCH 04/20] fix: possible fix for pixel drain and torbox cancel download --- python_rpc/http_downloader.py | 5 +++-- python_rpc/main.py | 16 ++++++++-------- python_rpc/torrent_downloader.py | 2 +- src/main/events/auth/sign-out.ts | 4 ---- src/main/services/download/download-manager.ts | 15 +++++++++++---- src/main/services/download/torbox.ts | 13 +++++++------ src/types/torbox.types.ts | 1 + 7 files changed, 31 insertions(+), 25 deletions(-) diff --git a/python_rpc/http_downloader.py b/python_rpc/http_downloader.py index 40e30ccdc..71e4b57ea 100644 --- a/python_rpc/http_downloader.py +++ b/python_rpc/http_downloader.py @@ -11,11 +11,12 @@ def __init__(self): ) ) - def start_download(self, url: str, save_path: str, header: str): + def start_download(self, url: str, save_path: str, header: str, out: str = None): if self.download: self.aria2.resume([self.download]) else: - downloads = self.aria2.add(url, options={"header": header, "dir": save_path}) + downloads = self.aria2.add(url, options={"header": header, "dir": save_path, "out": out}) + self.download = downloads[0] def pause_download(self): diff --git a/python_rpc/main.py b/python_rpc/main.py index 03df83dee..7b2c54b91 100644 --- a/python_rpc/main.py +++ b/python_rpc/main.py @@ -28,14 +28,14 @@ torrent_downloader = TorrentDownloader(torrent_session) downloads[initial_download['game_id']] = torrent_downloader try: - torrent_downloader.start_download(initial_download['url'], initial_download['save_path'], "") + torrent_downloader.start_download(initial_download['url'], initial_download['save_path']) except Exception as e: print("Error starting torrent download", e) else: http_downloader = HttpDownloader() downloads[initial_download['game_id']] = http_downloader try: - http_downloader.start_download(initial_download['url'], initial_download['save_path'], initial_download.get('header')) + http_downloader.start_download(initial_download['url'], initial_download['save_path'], initial_download.get('header'), initial_download.get("out")) except Exception as e: print("Error starting http download", e) @@ -45,7 +45,7 @@ torrent_downloader = TorrentDownloader(torrent_session, lt.torrent_flags.upload_mode) downloads[seed['game_id']] = torrent_downloader try: - torrent_downloader.start_download(seed['url'], seed['save_path'], "") + torrent_downloader.start_download(seed['url'], seed['save_path']) except Exception as e: print("Error starting seeding", e) @@ -140,18 +140,18 @@ def action(): if url.startswith('magnet'): if existing_downloader and isinstance(existing_downloader, TorrentDownloader): - existing_downloader.start_download(url, data['save_path'], "") + existing_downloader.start_download(url, data['save_path']) else: torrent_downloader = TorrentDownloader(torrent_session) downloads[game_id] = torrent_downloader - torrent_downloader.start_download(url, data['save_path'], "") + torrent_downloader.start_download(url, data['save_path']) else: if existing_downloader and isinstance(existing_downloader, HttpDownloader): - existing_downloader.start_download(url, data['save_path'], data.get('header')) + existing_downloader.start_download(url, data['save_path'], data.get('header'), data.get('out')) else: http_downloader = HttpDownloader() downloads[game_id] = http_downloader - http_downloader.start_download(url, data['save_path'], data.get('header')) + http_downloader.start_download(url, data['save_path'], data.get('header'), data.get('out')) downloading_game_id = game_id @@ -167,7 +167,7 @@ def action(): elif action == 'resume_seeding': torrent_downloader = TorrentDownloader(torrent_session, lt.torrent_flags.upload_mode) downloads[game_id] = torrent_downloader - torrent_downloader.start_download(data['url'], data['save_path'], "") + torrent_downloader.start_download(data['url'], data['save_path']) elif action == 'pause_seeding': downloader = downloads.get(game_id) if downloader: diff --git a/python_rpc/torrent_downloader.py b/python_rpc/torrent_downloader.py index ca4c2fa8f..8de8764ee 100644 --- a/python_rpc/torrent_downloader.py +++ b/python_rpc/torrent_downloader.py @@ -102,7 +102,7 @@ def __init__(self, torrent_session, flags = lt.torrent_flags.auto_managed): "http://bvarf.tracker.sh:2086/announce", ] - def start_download(self, magnet: str, save_path: str, header: str): + def start_download(self, magnet: str, save_path: str): params = {'url': magnet, 'save_path': save_path, 'trackers': self.trackers, 'flags': self.flags} self.torrent_handle = self.session.add_torrent(params) self.torrent_handle.resume() diff --git a/src/main/events/auth/sign-out.ts b/src/main/events/auth/sign-out.ts index 6b720015b..05fbaa864 100644 --- a/src/main/events/auth/sign-out.ts +++ b/src/main/events/auth/sign-out.ts @@ -2,7 +2,6 @@ import { registerEvent } from "../register-event"; import { DownloadManager, HydraApi, gamesPlaytime } from "@main/services"; import { dataSource } from "@main/data-source"; import { DownloadQueue, Game, UserAuth, UserSubscription } from "@main/entity"; -import { PythonRPC } from "@main/services/python-rpc"; const signOut = async (_event: Electron.IpcMainInvokeEvent) => { const databaseOperations = dataSource @@ -27,9 +26,6 @@ const signOut = async (_event: Electron.IpcMainInvokeEvent) => { /* Cancels any ongoing downloads */ DownloadManager.cancelDownload(); - /* Disconnects libtorrent */ - PythonRPC.kill(); - HydraApi.handleSignOut(); await Promise.all([ diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts index 1c91c4dce..902a0c4c4 100644 --- a/src/main/services/download/download-manager.ts +++ b/src/main/services/download/download-manager.ts @@ -21,6 +21,7 @@ import { RealDebridClient } from "./real-debrid"; import path from "path"; import { logger } from "../logger"; import { TorBoxClient } from "./torbox"; +import axios from "axios"; export class DownloadManager { private static downloadingGameId: number | null = null; @@ -262,11 +263,16 @@ export class DownloadManager { case Downloader.PixelDrain: { const id = game!.uri!.split("/").pop(); + const name = await axios + .get(`https://pixeldrain.com/api/file/${id}/info`) + .then((res) => res.data.name as string); + return { action: "start", game_id: game.id, url: `https://pixeldrain.com/api/file/${id}?download`, save_path: game.downloadPath!, + out: name, }; } case Downloader.Qiwi: { @@ -297,15 +303,16 @@ export class DownloadManager { }; } case Downloader.TorBox: { - const downloadUrl = await TorBoxClient.getDownloadUrl(game.uri!); - console.log(downloadUrl); + const { name, url } = await TorBoxClient.getDownloadInfo(game.uri!); + console.log(url, name); - if (!downloadUrl) return; + if (!url) return; return { action: "start", game_id: game.id, - url: downloadUrl, + url, save_path: game.downloadPath!, + out: name, }; } } diff --git a/src/main/services/download/torbox.ts b/src/main/services/download/torbox.ts index 0c0c0574f..7e0c90898 100644 --- a/src/main/services/download/torbox.ts +++ b/src/main/services/download/torbox.ts @@ -74,7 +74,7 @@ export class TorBoxClient { return response.data.data; } - static async getTorrentId(magnetUri: string) { + static async getTorrentIdAndName(magnetUri: string) { const userTorrents = await this.getAllTorrentsFromUser(); const { infoHash } = await parseTorrent(magnetUri); @@ -82,14 +82,15 @@ export class TorBoxClient { (userTorrent) => userTorrent.hash === infoHash ); - if (userTorrent) return userTorrent.id; + if (userTorrent) return { id: userTorrent.id, name: userTorrent.name }; const torrent = await this.addMagnet(magnetUri); - return torrent.torrent_id; + return { id: torrent.torrent_id, name: torrent.name }; } - static async getDownloadUrl(uri: string) { - const id = await this.getTorrentId(uri); - return this.requestLink(id); + static async getDownloadInfo(uri: string) { + const { id, name } = await this.getTorrentIdAndName(uri); + const url = await this.requestLink(id); + return { url, name: `${name}.zip` }; } } diff --git a/src/types/torbox.types.ts b/src/types/torbox.types.ts index a53ccc4c4..51e8bd127 100644 --- a/src/types/torbox.types.ts +++ b/src/types/torbox.types.ts @@ -66,6 +66,7 @@ export interface TorBoxAddTorrentRequest { torrent_id: number; name: string; hash: string; + size: number; }; } From abb16e77364b650b7c232aa4c2dd9941402b186d Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sat, 11 Jan 2025 15:19:20 -0300 Subject: [PATCH 05/20] chore: prettier --- src/main/services/hosters/datanodes.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/services/hosters/datanodes.ts b/src/main/services/hosters/datanodes.ts index d77e7d514..ae1444180 100644 --- a/src/main/services/hosters/datanodes.ts +++ b/src/main/services/hosters/datanodes.ts @@ -33,7 +33,8 @@ export class DatanodesApi { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36", }, - maxRedirects: 0, validateStatus: (status: number) => status === 302 || status < 400, + maxRedirects: 0, + validateStatus: (status: number) => status === 302 || status < 400, } ); From db2e31b8ccfd5b23f33bac0f3e6f77fd666ccfbd Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sat, 11 Jan 2025 17:52:48 -0300 Subject: [PATCH 06/20] feat: add torbox migration --- src/main/entity/user-preferences.entity.ts | 3 +++ src/main/knex-client.ts | 2 ++ src/main/main.ts | 6 ++++-- ...0250111182229_add_torbox_api_token_column.ts | 17 +++++++++++++++++ src/main/services/download/torbox.ts | 2 +- src/types/index.ts | 1 + 6 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 src/main/migrations/20250111182229_add_torbox_api_token_column.ts diff --git a/src/main/entity/user-preferences.entity.ts b/src/main/entity/user-preferences.entity.ts index a850b42fd..109ede5f3 100644 --- a/src/main/entity/user-preferences.entity.ts +++ b/src/main/entity/user-preferences.entity.ts @@ -20,6 +20,9 @@ export class UserPreferences { @Column("text", { nullable: true }) realDebridApiToken: string | null; + @Column("text", { nullable: true }) + torBoxApiToken: string | null; + @Column("boolean", { default: false }) downloadNotificationsEnabled: boolean; diff --git a/src/main/knex-client.ts b/src/main/knex-client.ts index 821efc808..c816c7c7b 100644 --- a/src/main/knex-client.ts +++ b/src/main/knex-client.ts @@ -17,6 +17,7 @@ import { AddShouldSeedColumn } from "./migrations/20241108200154_add_should_seed import { AddSeedAfterDownloadColumn } from "./migrations/20241108201806_add_seed_after_download"; import { AddHiddenAchievementDescriptionColumn } from "./migrations/20241216140633_add_hidden_achievement_description_column "; import { AddLaunchOptionsColumnToGame } from "./migrations/20241226044022_add_launch_options_column_to_game"; +import { AddTorBoxApiToken } from "./migrations/20250111182229_add_torbox_api_token_column"; export type HydraMigration = Knex.Migration & { name: string }; @@ -39,6 +40,7 @@ class MigrationSource implements Knex.MigrationSource { AddSeedAfterDownloadColumn, AddHiddenAchievementDescriptionColumn, AddLaunchOptionsColumnToGame, + AddTorBoxApiToken, ]); } getMigrationName(migration: HydraMigration): string { diff --git a/src/main/main.ts b/src/main/main.ts index 81916174e..d27f0cbd0 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -19,10 +19,12 @@ const loadState = async (userPreferences: UserPreferences | null) => { Aria2.spawn(); if (userPreferences?.realDebridApiToken) { - RealDebridClient.authorize(userPreferences?.realDebridApiToken); + RealDebridClient.authorize(userPreferences.realDebridApiToken); } - TorBoxClient.authorize("7371d5ec-52fa-4b87-9052-0c8c96d947cc"); + if (userPreferences?.torBoxApiToken) { + TorBoxClient.authorize(userPreferences?.torBoxApiToken); + } Ludusavi.addManifestToLudusaviConfig(); diff --git a/src/main/migrations/20250111182229_add_torbox_api_token_column.ts b/src/main/migrations/20250111182229_add_torbox_api_token_column.ts new file mode 100644 index 000000000..fc1904fd1 --- /dev/null +++ b/src/main/migrations/20250111182229_add_torbox_api_token_column.ts @@ -0,0 +1,17 @@ +import type { HydraMigration } from "@main/knex-client"; +import type { Knex } from "knex"; + +export const AddTorBoxApiToken: HydraMigration = { + name: "AddTorBoxApiToken", + up: (knex: Knex) => { + return knex.schema.alterTable("user_preferences", (table) => { + return table.string("torBoxApiToken").nullable(); + }); + }, + + down: async (knex: Knex) => { + return knex.schema.alterTable("user_preferences", (table) => { + return table.dropColumn("torBoxApiToken"); + }); + }, +}; diff --git a/src/main/services/download/torbox.ts b/src/main/services/download/torbox.ts index 7e0c90898..f0af52eb5 100644 --- a/src/main/services/download/torbox.ts +++ b/src/main/services/download/torbox.ts @@ -19,7 +19,7 @@ export class TorBoxClient { Authorization: `Bearer ${apiToken}`, }, }); - this.apiToken = "7371d5ec-52fa-4b87-9052-0c8c96d947cc"; + this.apiToken = apiToken; } static async addMagnet(magnet: string) { diff --git a/src/types/index.ts b/src/types/index.ts index 345893a5d..92cc566e6 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -163,6 +163,7 @@ export interface UserPreferences { repackUpdatesNotificationsEnabled: boolean; achievementNotificationsEnabled: boolean; realDebridApiToken: string | null; + torboxApiToken: string | null; preferQuitInsteadOfHiding: boolean; runAtStartup: boolean; startMinimized: boolean; From b1dde446b2cb6507311b5a6d019bb71baa1606bf Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sun, 12 Jan 2025 11:00:20 -0300 Subject: [PATCH 07/20] feat: few adjustments --- src/main/services/download/torbox.ts | 12 ++++++++---- src/types/index.ts | 2 +- src/types/torbox.types.ts | 4 ++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main/services/download/torbox.ts b/src/main/services/download/torbox.ts index f0af52eb5..b0d339dd9 100644 --- a/src/main/services/download/torbox.ts +++ b/src/main/services/download/torbox.ts @@ -10,19 +10,19 @@ import type { export class TorBoxClient { private static instance: AxiosInstance; private static readonly baseURL = "https://api.torbox.app/v1/api"; - public static apiToken: string; + private static apiToken: string; static authorize(apiToken: string) { + this.apiToken = apiToken; this.instance = axios.create({ baseURL: this.baseURL, headers: { Authorization: `Bearer ${apiToken}`, }, }); - this.apiToken = apiToken; } - static async addMagnet(magnet: string) { + private static async addMagnet(magnet: string) { const form = new FormData(); form.append("magnet", magnet); @@ -31,6 +31,10 @@ export class TorBoxClient { form ); + if (!response.data.success) { + throw new Error(response.data.detail); + } + return response.data.data; } @@ -74,7 +78,7 @@ export class TorBoxClient { return response.data.data; } - static async getTorrentIdAndName(magnetUri: string) { + private static async getTorrentIdAndName(magnetUri: string) { const userTorrents = await this.getAllTorrentsFromUser(); const { infoHash } = await parseTorrent(magnetUri); diff --git a/src/types/index.ts b/src/types/index.ts index 92cc566e6..b6fcbbb45 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -163,7 +163,7 @@ export interface UserPreferences { repackUpdatesNotificationsEnabled: boolean; achievementNotificationsEnabled: boolean; realDebridApiToken: string | null; - torboxApiToken: string | null; + torBoxApiToken: string | null; preferQuitInsteadOfHiding: boolean; runAtStartup: boolean; startMinimized: boolean; diff --git a/src/types/torbox.types.ts b/src/types/torbox.types.ts index 51e8bd127..ee72600aa 100644 --- a/src/types/torbox.types.ts +++ b/src/types/torbox.types.ts @@ -54,14 +54,14 @@ export interface TorBoxTorrentInfo { export interface TorBoxTorrentInfoRequest { success: boolean; detail: string; - error: string; + error: string | null; data: TorBoxTorrentInfo[]; } export interface TorBoxAddTorrentRequest { success: boolean; detail: string; - error: string; + error: string | null; data: { torrent_id: number; name: string; From 87613023842e0e412210022fc5d499f250b06dcf Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sun, 12 Jan 2025 11:01:18 -0300 Subject: [PATCH 08/20] feat: debrid token input component --- src/locales/en/translation.json | 3 +- src/locales/pt-BR/translation.json | 3 +- .../pages/settings/settings-debrid-input.tsx | 186 ++++++++++++++++++ ...l-debrid.css.ts => settings-debrid.css.ts} | 0 ...gs-real-debrid.tsx => settings-debrid.tsx} | 66 ++++++- src/renderer/src/pages/settings/settings.tsx | 6 +- src/types/index.ts | 2 + 7 files changed, 252 insertions(+), 14 deletions(-) create mode 100644 src/renderer/src/pages/settings/settings-debrid-input.tsx rename src/renderer/src/pages/settings/{settings-real-debrid.css.ts => settings-debrid.css.ts} (100%) rename src/renderer/src/pages/settings/{settings-real-debrid.tsx => settings-debrid.tsx} (65%) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 4e3dcb37c..233d04e46 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -280,7 +280,8 @@ "launch_minimized": "Launch Hydra minimized", "disable_nsfw_alert": "Disable NSFW alert", "seed_after_download_complete": "Seed after download complete", - "show_hidden_achievement_description": "Show hidden achievements description before unlocking them" + "show_hidden_achievement_description": "Show hidden achievements description before unlocking them", + "debrid_services": "Debrid Services" }, "notifications": { "download_complete": "Download complete", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 2a80084f3..453aff7ca 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -268,7 +268,8 @@ "launch_minimized": "Iniciar o Hydra minimizado", "disable_nsfw_alert": "Desativar alerta de conteúdo inapropriado", "seed_after_download_complete": "Semear após a conclusão do download", - "show_hidden_achievement_description": "Mostrar descrição de conquistas ocultas antes de debloqueá-las" + "show_hidden_achievement_description": "Mostrar descrição de conquistas ocultas antes de debloqueá-las", + "debrid_services": "Serviços Debrid" }, "notifications": { "download_complete": "Download concluído", diff --git a/src/renderer/src/pages/settings/settings-debrid-input.tsx b/src/renderer/src/pages/settings/settings-debrid-input.tsx new file mode 100644 index 000000000..5223e378a --- /dev/null +++ b/src/renderer/src/pages/settings/settings-debrid-input.tsx @@ -0,0 +1,186 @@ +import { useContext, useEffect, useMemo, useState } from "react"; +import { Trans, useTranslation } from "react-i18next"; +import { Button, CheckboxField, Link, TextField } from "@renderer/components"; +import * as styles from "./settings-debrid.css"; +import { useAppSelector, useToast } from "@renderer/hooks"; +import { SPACING_UNIT } from "@renderer/theme.css"; +import { settingsContext } from "@renderer/context"; +import { DebridServices } from "@types"; + +const REAL_DEBRID_API_TOKEN_URL = "https://real-debrid.com/apitoken"; +const TORBOX_API_TOKEN_URL = "https://torbox.app/settings"; + +interface SettingsDebridForm { + useRealDebrid: boolean; + realDebridApiToken: string | null; + useTorBox: boolean; + torBoxApiToken: string | null; +} + +export interface SettingsDebridProps { + service: DebridServices; + form: SettingsDebridForm; + setForm: (SettingsDebridForm) => void; +} + +export function SettingsDebridInput({ + service, + form, + setForm, +}: SettingsDebridProps) { + const userPreferences = useAppSelector( + (state) => state.userPreferences.value + ); + + const { updateUserPreferences } = useContext(settingsContext); + + const [isLoading, setIsLoading] = useState(false); + + const { showSuccessToast, showErrorToast } = useToast(); + + const { t } = useTranslation("settings"); + + useEffect(() => { + if (userPreferences) { + setForm({ + useRealDebrid: Boolean(userPreferences.realDebridApiToken), + realDebridApiToken: userPreferences.realDebridApiToken ?? null, + useTorBox: Boolean(userPreferences.torBoxApiToken), + torBoxApiToken: userPreferences.torBoxApiToken ?? null, + }); + } + }, [userPreferences]); + + const handleFormSubmit: React.FormEventHandler = async ( + event + ) => { + setIsLoading(true); + event.preventDefault(); + + try { + if (form.useRealDebrid) { + const user = await window.electron.authenticateRealDebrid( + form.realDebridApiToken! + ); + + if (user.type === "free") { + showErrorToast( + t("real_debrid_free_account_error", { username: user.username }) + ); + + return; + } else { + showSuccessToast( + t("real_debrid_linked_message", { username: user.username }) + ); + } + } else { + showSuccessToast(t("changes_saved")); + } + + updateUserPreferences({ + realDebridApiToken: form.useRealDebrid ? form.realDebridApiToken : null, + }); + } catch (err) { + showErrorToast(t("real_debrid_invalid_token")); + } finally { + setIsLoading(false); + } + }; + + const useDebridService = useMemo(() => { + if (service === "RealDebrid") { + return form.useRealDebrid; + } + + if (service === "TorBox") { + return form.useTorBox; + } + + return false; + }, [form, service]); + + const debridApiToken = useMemo(() => { + if (service === "RealDebrid") { + return form.realDebridApiToken; + } + + if (service === "TorBox") { + return form.torBoxApiToken; + } + + return null; + }, [form, service]); + + const onChangeCheckbox = () => { + if (service === "RealDebrid") { + setForm((prev) => ({ + ...prev, + useRealDebrid: !form.useRealDebrid, + })); + } + + if (service === "TorBox") { + setForm((prev) => ({ + ...prev, + useTorBox: !form.useTorBox, + })); + } + }; + + const onChangeInput = (event: React.ChangeEvent) => { + if (service === "RealDebrid") { + setForm((prev) => ({ + ...prev, + realDebridApiToken: event.target.value, + })); + } + + if (service === "TorBox") { + setForm((prev) => ({ + ...prev, + torBoxApiToken: event.target.value, + })); + } + }; + + const isButtonDisabled = + (form.useRealDebrid && !form.realDebridApiToken) || isLoading; + + return ( +
+ + + {useDebridService && ( + + {t("save")} + + } + hint={ + + + + } + /> + )} + + ); +} diff --git a/src/renderer/src/pages/settings/settings-real-debrid.css.ts b/src/renderer/src/pages/settings/settings-debrid.css.ts similarity index 100% rename from src/renderer/src/pages/settings/settings-real-debrid.css.ts rename to src/renderer/src/pages/settings/settings-debrid.css.ts diff --git a/src/renderer/src/pages/settings/settings-real-debrid.tsx b/src/renderer/src/pages/settings/settings-debrid.tsx similarity index 65% rename from src/renderer/src/pages/settings/settings-real-debrid.tsx rename to src/renderer/src/pages/settings/settings-debrid.tsx index 35804664e..9d63e68af 100644 --- a/src/renderer/src/pages/settings/settings-real-debrid.tsx +++ b/src/renderer/src/pages/settings/settings-debrid.tsx @@ -2,7 +2,7 @@ import { useContext, useEffect, useState } from "react"; import { Trans, useTranslation } from "react-i18next"; import { Button, CheckboxField, Link, TextField } from "@renderer/components"; -import * as styles from "./settings-real-debrid.css"; +import * as styles from "./settings-debrid.css"; import { useAppSelector, useToast } from "@renderer/hooks"; @@ -10,8 +10,9 @@ import { SPACING_UNIT } from "@renderer/theme.css"; import { settingsContext } from "@renderer/context"; const REAL_DEBRID_API_TOKEN_URL = "https://real-debrid.com/apitoken"; +const TORBOX_API_TOKEN_URL = "https://torbox.app/settings"; -export function SettingsRealDebrid() { +export function SettingsDebrid() { const userPreferences = useAppSelector( (state) => state.userPreferences.value ); @@ -22,6 +23,8 @@ export function SettingsRealDebrid() { const [form, setForm] = useState({ useRealDebrid: false, realDebridApiToken: null as string | null, + useTorBox: false, + torBoxApiToken: null as string | null, }); const { showSuccessToast, showErrorToast } = useToast(); @@ -33,6 +36,8 @@ export function SettingsRealDebrid() { setForm({ useRealDebrid: Boolean(userPreferences.realDebridApiToken), realDebridApiToken: userPreferences.realDebridApiToken ?? null, + useTorBox: Boolean(userPreferences.torBoxApiToken), + torBoxApiToken: userPreferences.torBoxApiToken ?? null, }); } }, [userPreferences]); @@ -102,6 +107,17 @@ export function SettingsRealDebrid() { } placeholder="API Token" containerProps={{ style: { marginTop: `${SPACING_UNIT}px` } }} + rightContent={ + + } hint={ @@ -110,13 +126,45 @@ export function SettingsRealDebrid() { /> )} - + + setForm((prev) => ({ + ...prev, + useTorBox: !form.useTorBox, + })) + } + /> + + {form.useTorBox && ( + + setForm({ ...form, torBoxApiToken: event.target.value }) + } + placeholder="API Token" + containerProps={{ style: { marginTop: `${SPACING_UNIT}px` } }} + rightContent={ + + } + hint={ + + + + } + /> + )} ); } diff --git a/src/renderer/src/pages/settings/settings.tsx b/src/renderer/src/pages/settings/settings.tsx index dffdfbaeb..00ceebd7a 100644 --- a/src/renderer/src/pages/settings/settings.tsx +++ b/src/renderer/src/pages/settings/settings.tsx @@ -2,7 +2,7 @@ import { Button } from "@renderer/components"; import * as styles from "./settings.css"; import { useTranslation } from "react-i18next"; -import { SettingsRealDebrid } from "./settings-real-debrid"; +import { SettingsDebrid } from "./settings-debrid"; import { SettingsGeneral } from "./settings-general"; import { SettingsBehavior } from "./settings-behavior"; @@ -25,7 +25,7 @@ export default function Settings() { t("general"), t("behavior"), t("download_sources"), - "Real-Debrid", + t("debrid_services"), ]; if (userDetails) return [...categories, t("privacy")]; @@ -50,7 +50,7 @@ export default function Settings() { } if (currentCategoryIndex === 3) { - return ; + return ; } return ; diff --git a/src/types/index.ts b/src/types/index.ts index b6fcbbb45..da5deea42 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -19,6 +19,8 @@ export type HydraCloudFeature = | "backup" | "achievements-points"; +export type DebridServices = "RealDebrid" | "TorBox"; + export interface GameRepack { id: number; title: string; From fc003841b00a2017886cc82cfadb019dc3ae2736 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sat, 1 Feb 2025 16:23:26 -0300 Subject: [PATCH 09/20] feat: merge adjustments --- src/main/events/auth/sign-out.ts | 1 - ...50111182229_add_torbox_api_token_column.ts | 17 -- .../services/download/download-manager.ts | 10 +- src/main/services/process-watcher.ts | 2 +- .../src/pages/downloads/download-group.tsx | 6 +- .../pages/settings/settings-debrid-input.tsx | 186 ------------------ src/types/index.ts | 18 -- 7 files changed, 10 insertions(+), 230 deletions(-) delete mode 100644 src/main/migrations/20250111182229_add_torbox_api_token_column.ts delete mode 100644 src/renderer/src/pages/settings/settings-debrid-input.tsx diff --git a/src/main/events/auth/sign-out.ts b/src/main/events/auth/sign-out.ts index e7f90aa54..2ab5e4581 100644 --- a/src/main/events/auth/sign-out.ts +++ b/src/main/events/auth/sign-out.ts @@ -1,6 +1,5 @@ import { registerEvent } from "../register-event"; import { DownloadManager, HydraApi, gamesPlaytime } from "@main/services"; -import { PythonRPC } from "@main/services/python-rpc"; import { db, downloadsSublevel, gamesSublevel, levelKeys } from "@main/level"; const signOut = async (_event: Electron.IpcMainInvokeEvent) => { diff --git a/src/main/migrations/20250111182229_add_torbox_api_token_column.ts b/src/main/migrations/20250111182229_add_torbox_api_token_column.ts deleted file mode 100644 index fc1904fd1..000000000 --- a/src/main/migrations/20250111182229_add_torbox_api_token_column.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { HydraMigration } from "@main/knex-client"; -import type { Knex } from "knex"; - -export const AddTorBoxApiToken: HydraMigration = { - name: "AddTorBoxApiToken", - up: (knex: Knex) => { - return knex.schema.alterTable("user_preferences", (table) => { - return table.string("torBoxApiToken").nullable(); - }); - }, - - down: async (knex: Knex) => { - return knex.schema.alterTable("user_preferences", (table) => { - return table.dropColumn("torBoxApiToken"); - }); - }, -}; diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts index e6ae91e29..c0145b419 100644 --- a/src/main/services/download/download-manager.ts +++ b/src/main/services/download/download-manager.ts @@ -271,7 +271,7 @@ export class DownloadManager { }; } case Downloader.PixelDrain: { - const id = game.uri!.split("/").pop(); + const id = download.uri.split("/").pop(); const name = await axios .get(`https://pixeldrain.com/api/file/${id}/info`) @@ -281,7 +281,7 @@ export class DownloadManager { action: "start", game_id: downloadId, url: `https://pixeldrain.com/api/file/${id}?download`, - save_path: game.downloadPath!, + save_path: download.downloadPath, out: name, }; } @@ -326,15 +326,15 @@ export class DownloadManager { }; } case Downloader.TorBox: { - const { name, url } = await TorBoxClient.getDownloadInfo(game.uri!); + const { name, url } = await TorBoxClient.getDownloadInfo(download.uri); console.log(url, name); if (!url) return; return { action: "start", - game_id: game.id, + game_id: downloadId, url, - save_path: game.downloadPath!, + save_path: download.downloadPath, out: name, }; } diff --git a/src/main/services/process-watcher.ts b/src/main/services/process-watcher.ts index bfa5cc196..0cf4605f7 100644 --- a/src/main/services/process-watcher.ts +++ b/src/main/services/process-watcher.ts @@ -285,7 +285,7 @@ const onCloseGame = (game: Game) => { if (game.remoteId) { // create backup // todo: check for hydra cloud? - createBackup(game.objectID, game.shop, ""); + createBackup(game.objectId, game.shop, ""); updateGamePlaytime( game, diff --git a/src/renderer/src/pages/downloads/download-group.tsx b/src/renderer/src/pages/downloads/download-group.tsx index 85ace39bb..687946507 100644 --- a/src/renderer/src/pages/downloads/download-group.tsx +++ b/src/renderer/src/pages/downloads/download-group.tsx @@ -300,7 +300,7 @@ export function DownloadGroup({ />
- {game.downloader === Downloader.TorBox ? ( + {game.download?.downloader === Downloader.TorBox ? (
TorBox
) : ( - {DOWNLOADER_NAME[game.downloader]} + + {DOWNLOADER_NAME[game.download!.downloader]} + )}
diff --git a/src/renderer/src/pages/settings/settings-debrid-input.tsx b/src/renderer/src/pages/settings/settings-debrid-input.tsx deleted file mode 100644 index 5223e378a..000000000 --- a/src/renderer/src/pages/settings/settings-debrid-input.tsx +++ /dev/null @@ -1,186 +0,0 @@ -import { useContext, useEffect, useMemo, useState } from "react"; -import { Trans, useTranslation } from "react-i18next"; -import { Button, CheckboxField, Link, TextField } from "@renderer/components"; -import * as styles from "./settings-debrid.css"; -import { useAppSelector, useToast } from "@renderer/hooks"; -import { SPACING_UNIT } from "@renderer/theme.css"; -import { settingsContext } from "@renderer/context"; -import { DebridServices } from "@types"; - -const REAL_DEBRID_API_TOKEN_URL = "https://real-debrid.com/apitoken"; -const TORBOX_API_TOKEN_URL = "https://torbox.app/settings"; - -interface SettingsDebridForm { - useRealDebrid: boolean; - realDebridApiToken: string | null; - useTorBox: boolean; - torBoxApiToken: string | null; -} - -export interface SettingsDebridProps { - service: DebridServices; - form: SettingsDebridForm; - setForm: (SettingsDebridForm) => void; -} - -export function SettingsDebridInput({ - service, - form, - setForm, -}: SettingsDebridProps) { - const userPreferences = useAppSelector( - (state) => state.userPreferences.value - ); - - const { updateUserPreferences } = useContext(settingsContext); - - const [isLoading, setIsLoading] = useState(false); - - const { showSuccessToast, showErrorToast } = useToast(); - - const { t } = useTranslation("settings"); - - useEffect(() => { - if (userPreferences) { - setForm({ - useRealDebrid: Boolean(userPreferences.realDebridApiToken), - realDebridApiToken: userPreferences.realDebridApiToken ?? null, - useTorBox: Boolean(userPreferences.torBoxApiToken), - torBoxApiToken: userPreferences.torBoxApiToken ?? null, - }); - } - }, [userPreferences]); - - const handleFormSubmit: React.FormEventHandler = async ( - event - ) => { - setIsLoading(true); - event.preventDefault(); - - try { - if (form.useRealDebrid) { - const user = await window.electron.authenticateRealDebrid( - form.realDebridApiToken! - ); - - if (user.type === "free") { - showErrorToast( - t("real_debrid_free_account_error", { username: user.username }) - ); - - return; - } else { - showSuccessToast( - t("real_debrid_linked_message", { username: user.username }) - ); - } - } else { - showSuccessToast(t("changes_saved")); - } - - updateUserPreferences({ - realDebridApiToken: form.useRealDebrid ? form.realDebridApiToken : null, - }); - } catch (err) { - showErrorToast(t("real_debrid_invalid_token")); - } finally { - setIsLoading(false); - } - }; - - const useDebridService = useMemo(() => { - if (service === "RealDebrid") { - return form.useRealDebrid; - } - - if (service === "TorBox") { - return form.useTorBox; - } - - return false; - }, [form, service]); - - const debridApiToken = useMemo(() => { - if (service === "RealDebrid") { - return form.realDebridApiToken; - } - - if (service === "TorBox") { - return form.torBoxApiToken; - } - - return null; - }, [form, service]); - - const onChangeCheckbox = () => { - if (service === "RealDebrid") { - setForm((prev) => ({ - ...prev, - useRealDebrid: !form.useRealDebrid, - })); - } - - if (service === "TorBox") { - setForm((prev) => ({ - ...prev, - useTorBox: !form.useTorBox, - })); - } - }; - - const onChangeInput = (event: React.ChangeEvent) => { - if (service === "RealDebrid") { - setForm((prev) => ({ - ...prev, - realDebridApiToken: event.target.value, - })); - } - - if (service === "TorBox") { - setForm((prev) => ({ - ...prev, - torBoxApiToken: event.target.value, - })); - } - }; - - const isButtonDisabled = - (form.useRealDebrid && !form.realDebridApiToken) || isLoading; - - return ( -
- - - {useDebridService && ( - - {t("save")} - - } - hint={ - - - - } - /> - )} - - ); -} diff --git a/src/types/index.ts b/src/types/index.ts index f436551ff..75000f362 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -55,24 +55,6 @@ export interface GameRunning { sessionDurationInMillis: number; } -export interface DownloadProgress { - downloadSpeed: number; - timeRemaining: number; - numPeers: number; - numSeeds: number; - isDownloadingMetadata: boolean; - isCheckingFiles: boolean; - progress: number; - gameId: number; - game: LibraryGame; -} - -export interface SeedingStatus { - gameId: number; - status: GameStatus; - uploadSpeed: number; -} - export interface UserPreferences { downloadsPath: string | null; language: string; From d8e322e022f03702ea337f2c450fedeaa4aea701 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sat, 1 Feb 2025 16:47:10 -0300 Subject: [PATCH 10/20] feat: save torbox token --- .../user-preferences/get-user-preferences.ts | 6 ++++++ .../user-preferences/update-user-preferences.ts | 4 ++++ src/main/main.ts | 5 +++++ .../src/pages/downloads/download-group.tsx | 11 ++++++++--- .../modals/download-settings-modal.tsx | 2 ++ .../src/pages/settings/settings-debrid.tsx | 1 + src/types/index.ts | 16 ---------------- src/types/level.types.ts | 1 + 8 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/main/events/user-preferences/get-user-preferences.ts b/src/main/events/user-preferences/get-user-preferences.ts index b40d6780f..c67f72b97 100644 --- a/src/main/events/user-preferences/get-user-preferences.ts +++ b/src/main/events/user-preferences/get-user-preferences.ts @@ -15,6 +15,12 @@ const getUserPreferences = async () => ); } + if (userPreferences?.torBoxApiToken) { + userPreferences.torBoxApiToken = Crypto.decrypt( + userPreferences.torBoxApiToken + ); + } + return userPreferences; }); diff --git a/src/main/events/user-preferences/update-user-preferences.ts b/src/main/events/user-preferences/update-user-preferences.ts index 311935581..275a6f276 100644 --- a/src/main/events/user-preferences/update-user-preferences.ts +++ b/src/main/events/user-preferences/update-user-preferences.ts @@ -30,6 +30,10 @@ const updateUserPreferences = async ( ); } + if (preferences.torBoxApiToken) { + preferences.torBoxApiToken = Crypto.encrypt(preferences.torBoxApiToken); + } + if (!preferences.downloadsPath) { preferences.downloadsPath = null; } diff --git a/src/main/main.ts b/src/main/main.ts index 0777a28ed..4824a1a57 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -20,6 +20,7 @@ import { } from "./level"; import { Auth, User, type UserPreferences } from "@types"; import { knexClient } from "./knex-client"; +import { TorBoxClient } from "./services/download/torbox"; export const loadState = async () => { const userPreferences = await migrateFromSqlite().then(async () => { @@ -42,6 +43,10 @@ export const loadState = async () => { ); } + if (userPreferences?.torBoxApiToken) { + TorBoxClient.authorize(Crypto.decrypt(userPreferences.torBoxApiToken)); + } + Ludusavi.addManifestToLudusaviConfig(); HydraApi.setupApi().then(() => { diff --git a/src/renderer/src/pages/downloads/download-group.tsx b/src/renderer/src/pages/downloads/download-group.tsx index 0bcbbdbe1..d84d66014 100644 --- a/src/renderer/src/pages/downloads/download-group.tsx +++ b/src/renderer/src/pages/downloads/download-group.tsx @@ -31,6 +31,7 @@ import { } from "@primer/octicons-react"; import torBoxLogo from "@renderer/assets/icons/torbox.webp"; +import { SPACING_UNIT, vars } from "@renderer/theme.css"; export interface DownloadGroupProps { library: LibraryGame[]; @@ -237,12 +238,16 @@ export function DownloadGroup({ ]; } + const isResumeDisabled = + (download?.downloader === Downloader.RealDebrid && + !userPreferences?.realDebridApiToken) || + (download?.downloader === Downloader.TorBox && + !userPreferences?.torBoxApiToken); + return [ { label: t("resume"), - disabled: - download?.downloader === Downloader.RealDebrid && - !userPreferences?.realDebridApiToken, + disabled: isResumeDisabled, onClick: () => { resumeDownload(game.shop, game.objectId); }, diff --git a/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx b/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx index 89283ebbc..dae20d4c2 100644 --- a/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/download-settings-modal.tsx @@ -88,6 +88,8 @@ export function DownloadSettingsModal({ const filteredDownloaders = downloaders.filter((downloader) => { if (downloader === Downloader.RealDebrid) return userPreferences?.realDebridApiToken; + if (downloader === Downloader.TorBox) + return userPreferences?.torBoxApiToken; return true; }); diff --git a/src/renderer/src/pages/settings/settings-debrid.tsx b/src/renderer/src/pages/settings/settings-debrid.tsx index 9d63e68af..ac6a09ffb 100644 --- a/src/renderer/src/pages/settings/settings-debrid.tsx +++ b/src/renderer/src/pages/settings/settings-debrid.tsx @@ -71,6 +71,7 @@ export function SettingsDebrid() { updateUserPreferences({ realDebridApiToken: form.useRealDebrid ? form.realDebridApiToken : null, + torBoxApiToken: form.useTorBox ? form.torBoxApiToken : null, }); } catch (err) { showErrorToast(t("real_debrid_invalid_token")); diff --git a/src/types/index.ts b/src/types/index.ts index 75000f362..1f189a8da 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -55,22 +55,6 @@ export interface GameRunning { sessionDurationInMillis: number; } -export interface UserPreferences { - downloadsPath: string | null; - language: string; - downloadNotificationsEnabled: boolean; - repackUpdatesNotificationsEnabled: boolean; - achievementNotificationsEnabled: boolean; - realDebridApiToken: string | null; - torBoxApiToken: string | null; - preferQuitInsteadOfHiding: boolean; - runAtStartup: boolean; - startMinimized: boolean; - disableNsfwAlert: boolean; - seedAfterDownloadComplete: boolean; - showHiddenAchievementsDescription: boolean; -} - export interface Steam250Game { title: string; objectId: string; diff --git a/src/types/level.types.ts b/src/types/level.types.ts index 28d8b0b6e..aa28d9dd2 100644 --- a/src/types/level.types.ts +++ b/src/types/level.types.ts @@ -69,6 +69,7 @@ export interface UserPreferences { downloadsPath?: string | null; language?: string; realDebridApiToken?: string | null; + torBoxApiToken?: string | null; preferQuitInsteadOfHiding?: boolean; runAtStartup?: boolean; startMinimized?: boolean; From ddbb2483c6792137bff92958d465feaea91694ef Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sat, 1 Feb 2025 17:02:48 -0300 Subject: [PATCH 11/20] feat: adjustments --- src/locales/en/translation.json | 3 ++- src/locales/pt-BR/translation.json | 3 ++- src/main/services/download/download-manager.ts | 1 - src/main/services/download/torbox.ts | 9 ++++++--- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index cf7a313e4..54047d337 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -297,7 +297,8 @@ "become_subscriber": "Be Hydra Cloud", "subscription_renew_cancelled": "Automatic renewal is disabled", "subscription_renews_on": "Your subscription renews on {{date}}", - "bill_sent_until": "Your next bill will be sent until this day" + "bill_sent_until": "Your next bill will be sent until this day", + "enable_torbox": "Enable Torbox" }, "notifications": { "download_complete": "Download complete", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 6392937e1..af0747ee9 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -286,7 +286,8 @@ "become_subscriber": "Seja Hydra Cloud", "subscription_renew_cancelled": "A renovação automática está desativada", "subscription_renews_on": "Sua assinatura renova dia {{date}}", - "bill_sent_until": "Sua próxima cobrança será enviada até esse dia" + "bill_sent_until": "Sua próxima cobrança será enviada até esse dia", + "enable_torbox": "Habilitar Torbox" }, "notifications": { "download_complete": "Download concluído", diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts index cc2890678..b358d208e 100644 --- a/src/main/services/download/download-manager.ts +++ b/src/main/services/download/download-manager.ts @@ -330,7 +330,6 @@ export class DownloadManager { } case Downloader.TorBox: { const { name, url } = await TorBoxClient.getDownloadInfo(download.uri); - console.log(url, name); if (!url) return; return { diff --git a/src/main/services/download/torbox.ts b/src/main/services/download/torbox.ts index b0d339dd9..8011cae8a 100644 --- a/src/main/services/download/torbox.ts +++ b/src/main/services/download/torbox.ts @@ -93,8 +93,11 @@ export class TorBoxClient { } static async getDownloadInfo(uri: string) { - const { id, name } = await this.getTorrentIdAndName(uri); - const url = await this.requestLink(id); - return { url, name: `${name}.zip` }; + const torrentData = await this.getTorrentIdAndName(uri); + const url = await this.requestLink(torrentData.id); + + const name = torrentData.name ? `${torrentData.name}.zip` : undefined; + + return { url, name }; } } From ebc70ce28f1e6563ca445db9901a3253e06fdf0d Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sat, 1 Feb 2025 17:42:49 -0300 Subject: [PATCH 12/20] feat: own tab to torbox --- src/locales/ar/translation.json | 8 +- src/locales/bg/translation.json | 8 +- src/locales/ca/translation.json | 8 +- src/locales/cs/translation.json | 8 +- src/locales/da/translation.json | 8 +- src/locales/de/translation.json | 8 +- src/locales/en/translation.json | 12 +- src/locales/es/translation.json | 8 +- src/locales/et/translation.json | 8 +- src/locales/fa/translation.json | 2 +- src/locales/id/translation.json | 8 +- src/locales/it/translation.json | 2 +- src/locales/kk/translation.json | 8 +- src/locales/ko/translation.json | 2 +- src/locales/nb/translation.json | 8 +- src/locales/nl/translation.json | 2 +- src/locales/pl/translation.json | 2 +- src/locales/pt-BR/translation.json | 12 +- src/locales/pt-PT/translation.json | 8 +- src/locales/ro/translation.json | 8 +- src/locales/ru/translation.json | 8 +- src/locales/tr/translation.json | 8 +- src/locales/uk/translation.json | 8 +- src/locales/zh/translation.json | 8 +- src/main/events/index.ts | 1 + .../user-preferences/authenticate-torbox.ts | 14 +++ src/preload/index.ts | 2 + src/renderer/src/declaration.d.ts | 2 + ...rid.css.ts => settings-real-debrid.css.ts} | 0 ...gs-debrid.tsx => settings-real-debrid.tsx} | 64 ++-------- .../src/pages/settings/settings-torbox.css.ts | 13 ++ .../src/pages/settings/settings-torbox.tsx | 119 ++++++++++++++++++ src/renderer/src/pages/settings/settings.tsx | 12 +- 33 files changed, 256 insertions(+), 141 deletions(-) create mode 100644 src/main/events/user-preferences/authenticate-torbox.ts rename src/renderer/src/pages/settings/{settings-debrid.css.ts => settings-real-debrid.css.ts} (100%) rename src/renderer/src/pages/settings/{settings-debrid.tsx => settings-real-debrid.tsx} (63%) create mode 100644 src/renderer/src/pages/settings/settings-torbox.css.ts create mode 100644 src/renderer/src/pages/settings/settings-torbox.tsx diff --git a/src/locales/ar/translation.json b/src/locales/ar/translation.json index 15a24a7dd..a12b3559b 100644 --- a/src/locales/ar/translation.json +++ b/src/locales/ar/translation.json @@ -236,13 +236,13 @@ "behavior": "السلوك", "download_sources": "مصادر التنزيل", "language": "اللغة", - "real_debrid_api_token": "رمز API", + "api_token": "رمز API", "enable_real_debrid": "تفعيل Real-Debrid", "real_debrid_description": "Real-Debrid هو أداة تنزيل غير مقيدة تتيح لك تنزيل الملفات بسرعة، مقيدة فقط بسرعة الإنترنت لديك.", - "real_debrid_invalid_token": "رمز API غير صالح", - "real_debrid_api_token_hint": "يمكنك الحصول على رمز API الخاص بك <0>هنا", + "debrid_invalid_token": "رمز API غير صالح", + "debrid_api_token_hint": "يمكنك الحصول على رمز API الخاص بك <0>هنا", "real_debrid_free_account_error": "الحساب \"{{username}}\" هو حساب مجاني. يرجى الاشتراك في Real-Debrid", - "real_debrid_linked_message": "تم ربط الحساب \"{{username}}\"", + "debrid_linked_message": "تم ربط الحساب \"{{username}}\"", "save_changes": "حفظ التغييرات", "changes_saved": "تم حفظ التغييرات بنجاح", "download_sources_description": "سيقوم Hydra بجلب روابط التنزيل من هذه المصادر. يجب أن يكون عنوان URL المصدر رابطًا مباشرًا لملف .json يحتوي على روابط التنزيل.", diff --git a/src/locales/bg/translation.json b/src/locales/bg/translation.json index b68e60da4..857d3ed4b 100644 --- a/src/locales/bg/translation.json +++ b/src/locales/bg/translation.json @@ -230,13 +230,13 @@ "behavior": "Поведение", "download_sources": "Източници за изтегляне", "language": "Език", - "real_debrid_api_token": "API Токен", + "api_token": "API Токен", "enable_real_debrid": "Включи Real-Debrid", "real_debrid_description": "Real-Debrid е неограничен даунлоудър, който ви позволява бързо да изтегляте файлове, ограничени само от скоростта на интернет..", - "real_debrid_invalid_token": "Невалиден API токен", - "real_debrid_api_token_hint": "Вземете своя API токен <0>тук", + "debrid_invalid_token": "Невалиден API токен", + "debrid_api_token_hint": "Вземете своя API токен <0>тук", "real_debrid_free_account_error": "Акаунтът \"{{username}}\" е безплатен акаунт. Моля абонирай се за Real-Debrid", - "real_debrid_linked_message": "Акаунтът \"{{username}}\" е свързан", + "debrid_linked_message": "Акаунтът \"{{username}}\" е свързан", "save_changes": "Запази промените", "changes_saved": "Промените са успешно запазни", "download_sources_description": "Hydra ще извлича връзките за изтегляне от тези източници. URL адресът на източника трябва да е директна връзка към .json файл, съдържащ връзките за изтегляне.", diff --git a/src/locales/ca/translation.json b/src/locales/ca/translation.json index acf4b3c7b..6d689f2b0 100644 --- a/src/locales/ca/translation.json +++ b/src/locales/ca/translation.json @@ -161,13 +161,13 @@ "behavior": "Comportament", "download_sources": "Fonts de descàrrega", "language": "Idioma", - "real_debrid_api_token": "Testimoni API", + "api_token": "Testimoni API", "enable_real_debrid": "Activa el Real Debrid", "real_debrid_description": "Real-Debrid és un programa de descàrrega sense restriccions que us permet descarregar fitxers a l'instant i al màxim de la vostra velocitat d'Internet.", - "real_debrid_invalid_token": "Invalida el testimoni de l'API", - "real_debrid_api_token_hint": "Pots obtenir la teva clau de l'API <0>aquí.", + "debrid_invalid_token": "Invalida el testimoni de l'API", + "debrid_api_token_hint": "Pots obtenir la teva clau de l'API <0>aquí.", "real_debrid_free_account_error": "L'usuari \"{{username}}\" és un compte gratuït. Si us plau subscriu-te a Real-Debrid", - "real_debrid_linked_message": "Compte \"{{username}}\" vinculat", + "debrid_linked_message": "Compte \"{{username}}\" vinculat", "save_changes": "Desa els canvis", "changes_saved": "Els canvis s'han desat correctament", "download_sources_description": "Hydra buscarà els enllaços de descàrrega d'aquestes fonts. L'URL d'origen ha de ser un enllaç directe a un fitxer .json que contingui els enllaços de descàrrega.", diff --git a/src/locales/cs/translation.json b/src/locales/cs/translation.json index c12914446..b3543f94d 100644 --- a/src/locales/cs/translation.json +++ b/src/locales/cs/translation.json @@ -214,13 +214,13 @@ "behavior": "Chování", "download_sources": "Zdroje stahování", "language": "Jazyk", - "real_debrid_api_token": "API Token", + "api_token": "API Token", "enable_real_debrid": "Povolit Real-Debrid", "real_debrid_description": "Real-Debrid je neomezený správce stahování, který umožňuje stahovat soubory v nejvyšší rychlosti vašeho internetu.", - "real_debrid_invalid_token": "Neplatný API token", - "real_debrid_api_token_hint": "API token můžeš sehnat <0>zde", + "debrid_invalid_token": "Neplatný API token", + "debrid_api_token_hint": "API token můžeš sehnat <0>zde", "real_debrid_free_account_error": "Účet \"{{username}}\" má základní úroveň. Prosím předplaťte si Real-Debrid", - "real_debrid_linked_message": "Účet \"{{username}}\" je propojen", + "debrid_linked_message": "Účet \"{{username}}\" je propojen", "save_changes": "Uložit změny", "changes_saved": "Změny úspěšně uloženy", "download_sources_description": "Hydra bude odsud sbírat soubory. Zdrojový odkaz musí být .json soubor obsahující odkazy na soubory.", diff --git a/src/locales/da/translation.json b/src/locales/da/translation.json index 711c81a34..9a7c700f7 100644 --- a/src/locales/da/translation.json +++ b/src/locales/da/translation.json @@ -177,13 +177,13 @@ "behavior": "Opførsel", "download_sources": "Download kilder", "language": "Sprog", - "real_debrid_api_token": "API nøgle", + "api_token": "API nøgle", "enable_real_debrid": "Slå Real-Debrid til", "real_debrid_description": "Real-Debrid er en ubegrænset downloader der gør det muligt for dig at downloade filer med det samme og med den bedste udnyttelse af din internet hastighed.", - "real_debrid_invalid_token": "Ugyldig API nøgle", - "real_debrid_api_token_hint": "Du kan få din API nøgle <0>her", + "debrid_invalid_token": "Ugyldig API nøgle", + "debrid_api_token_hint": "Du kan få din API nøgle <0>her", "real_debrid_free_account_error": "Brugeren \"{{username}}\" er en gratis bruger. Venligst abbonér på Real-Debrid", - "real_debrid_linked_message": "Brugeren \"{{username}}\" er forbundet", + "debrid_linked_message": "Brugeren \"{{username}}\" er forbundet", "save_changes": "Gem ændringer", "changes_saved": "Ændringer gemt successfuldt", "download_sources_description": "Hydra vil hente download links fra disse kilder. Kilde URLen skal være et direkte link til en .json fil der indeholder download linkene.", diff --git a/src/locales/de/translation.json b/src/locales/de/translation.json index bf1eff601..c94b09136 100644 --- a/src/locales/de/translation.json +++ b/src/locales/de/translation.json @@ -161,13 +161,13 @@ "behavior": "Verhalten", "download_sources": "Download-Quellen", "language": "Sprache", - "real_debrid_api_token": "API Token", + "api_token": "API Token", "enable_real_debrid": "Real-Debrid aktivieren", "real_debrid_description": "Real-Debrid ist ein unrestriktiver Downloader, der es dir ermöglicht Dateien sofort und mit deiner maximalen Internetgeschwindigkeit herunterzuladen.", - "real_debrid_invalid_token": "API token nicht gültig", - "real_debrid_api_token_hint": "<0>Hier kannst du dir deinen API Token holen", + "debrid_invalid_token": "API token nicht gültig", + "debrid_api_token_hint": "<0>Hier kannst du dir deinen API Token holen", "real_debrid_free_account_error": "Das Konto \"{{username}}\" ist ein gratis account. Bitte abonniere Real-Debrid", - "real_debrid_linked_message": "Konto \"{{username}}\" verknüpft", + "debrid_linked_message": "Konto \"{{username}}\" verknüpft", "save_changes": "Änderungen speichern", "changes_saved": "Änderungen erfolgreich gespeichert", "download_sources_description": "Hydra wird die Download-Links von diesen Quellen abrufen. Die Quell-URL muss ein direkter Link zu einer .json Datei, welche die Download-Links enthält, sein.", diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 54047d337..a1914c58a 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -236,13 +236,13 @@ "behavior": "Behavior", "download_sources": "Download sources", "language": "Language", - "real_debrid_api_token": "API Token", + "api_token": "API Token", "enable_real_debrid": "Enable Real-Debrid", "real_debrid_description": "Real-Debrid is an unrestricted downloader that allows you to quickly download files, only limited by your internet speed.", - "real_debrid_invalid_token": "Invalid API token", - "real_debrid_api_token_hint": "You can get your API token <0>here", + "debrid_invalid_token": "Invalid API token", + "debrid_api_token_hint": "You can get your API token <0>here", "real_debrid_free_account_error": "The account \"{{username}}\" is a free account. Please subscribe to Real-Debrid", - "real_debrid_linked_message": "Account \"{{username}}\" linked", + "debrid_linked_message": "Account \"{{username}}\" linked", "save_changes": "Save changes", "changes_saved": "Changes successfully saved", "download_sources_description": "Hydra will fetch the download links from these sources. The source URL must be a direct link to a .json file containing the download links.", @@ -281,7 +281,6 @@ "disable_nsfw_alert": "Disable NSFW alert", "seed_after_download_complete": "Seed after download complete", "show_hidden_achievement_description": "Show hidden achievements description before unlocking them", - "debrid_services": "Debrid Services", "account": "Account", "no_users_blocked": "You have no blocked users", "subscription_active_until": "Your Hydra Cloud is active until {{date}}", @@ -298,7 +297,8 @@ "subscription_renew_cancelled": "Automatic renewal is disabled", "subscription_renews_on": "Your subscription renews on {{date}}", "bill_sent_until": "Your next bill will be sent until this day", - "enable_torbox": "Enable Torbox" + "enable_torbox": "Enable Torbox", + "torbox_description": "TorBox is your premium seedbox service rivaling even the best servers on the market." }, "notifications": { "download_complete": "Download complete", diff --git a/src/locales/es/translation.json b/src/locales/es/translation.json index c8667d6c4..43839e9eb 100644 --- a/src/locales/es/translation.json +++ b/src/locales/es/translation.json @@ -236,13 +236,13 @@ "behavior": "Otros", "download_sources": "Fuentes de descarga", "language": "Idioma", - "real_debrid_api_token": "Token API", + "api_token": "Token API", "enable_real_debrid": "Activar Real-Debrid", "real_debrid_description": "Real-Debrid es una forma de descargar sin restricciones archivos instantáneamente con la máxima velocidad de tu internet.", - "real_debrid_invalid_token": "Token de API inválido", - "real_debrid_api_token_hint": "Puedes obtener tu clave de API <0>aquí", + "debrid_invalid_token": "Token de API inválido", + "debrid_api_token_hint": "Puedes obtener tu clave de API <0>aquí", "real_debrid_free_account_error": "La cuenta \"{{username}}\" es una cuenta gratuita. Por favor, suscríbete a Real-Debrid", - "real_debrid_linked_message": "Cuenta \"{{username}}\" vinculada", + "debrid_linked_message": "Cuenta \"{{username}}\" vinculada", "save_changes": "Guardar cambios", "changes_saved": "Ajustes guardados exitosamente", "download_sources_description": "Hydra buscará los enlaces de descarga de estas fuentes. La URL de origen debe ser un enlace directo a un archivo .json que contenga los enlaces de descarga", diff --git a/src/locales/et/translation.json b/src/locales/et/translation.json index 91b4a63a8..97e69a90a 100644 --- a/src/locales/et/translation.json +++ b/src/locales/et/translation.json @@ -213,13 +213,13 @@ "behavior": "Käitumine", "download_sources": "Allalaadimise allikad", "language": "Keel", - "real_debrid_api_token": "API Võti", + "api_token": "API Võti", "enable_real_debrid": "Luba Real-Debrid", "real_debrid_description": "Real-Debrid on piiranguteta allalaadija, mis võimaldab sul faile alla laadida koheselt ja sinu internetiühenduse parima kiirusega.", - "real_debrid_invalid_token": "Vigane API võti", - "real_debrid_api_token_hint": "Sa saad oma API võtme <0>siit", + "debrid_invalid_token": "Vigane API võti", + "debrid_api_token_hint": "Sa saad oma API võtme <0>siit", "real_debrid_free_account_error": "Konto \"{{username}}\" on tasuta konto. Palun telli Real-Debrid", - "real_debrid_linked_message": "Konto \"{{username}}\" ühendatud", + "debrid_linked_message": "Konto \"{{username}}\" ühendatud", "save_changes": "Salvesta muudatused", "changes_saved": "Muudatused edukalt salvestatud", "download_sources_description": "Hydra laeb allalaadimise lingid nendest allikatest. Allika URL peab olema otsene link .json failile, mis sisaldab allalaadimise linke.", diff --git a/src/locales/fa/translation.json b/src/locales/fa/translation.json index 2409635f4..9d4f7280b 100644 --- a/src/locales/fa/translation.json +++ b/src/locales/fa/translation.json @@ -110,7 +110,7 @@ "general": "کلی", "behavior": "رفتار", "enable_real_debrid": "فعال‌سازی Real-Debrid", - "real_debrid_api_token_hint": "کلید API خود را از <ب0>اینجا بگیرید.", + "debrid_api_token_hint": "کلید API خود را از <ب0>اینجا بگیرید.", "save_changes": "ذخیره تغییرات" }, "notifications": { diff --git a/src/locales/id/translation.json b/src/locales/id/translation.json index ba4a06f19..a813d770c 100644 --- a/src/locales/id/translation.json +++ b/src/locales/id/translation.json @@ -161,13 +161,13 @@ "behavior": "Perilaku", "download_sources": "Sumber unduhan", "language": "Bahasa", - "real_debrid_api_token": "Token API", + "api_token": "Token API", "enable_real_debrid": "Aktifkan Real-Debrid", "real_debrid_description": "Real-Debrid adalah downloader tanpa batas yang memungkinkan kamu untuk mengunduh file dengan cepat dan pada kecepatan terbaik dari Internet kamu.", - "real_debrid_invalid_token": "Token API tidak valid", - "real_debrid_api_token_hint": "Kamu bisa dapatkan token API di <0>sini", + "debrid_invalid_token": "Token API tidak valid", + "debrid_api_token_hint": "Kamu bisa dapatkan token API di <0>sini", "real_debrid_free_account_error": "Akun \"{{username}}\" adalah akun gratis. Silakan berlangganan Real-Debrid", - "real_debrid_linked_message": "Akun \"{{username}}\" terhubung", + "debrid_linked_message": "Akun \"{{username}}\" terhubung", "save_changes": "Simpan perubahan", "changes_saved": "Perubahan disimpan berhasil", "download_sources_description": "Hydra akan mencari link unduhan dari sini. URL harus menuju file .json dengan link unduhan.", diff --git a/src/locales/it/translation.json b/src/locales/it/translation.json index cf763320f..742f889f0 100644 --- a/src/locales/it/translation.json +++ b/src/locales/it/translation.json @@ -118,7 +118,7 @@ "general": "Generale", "behavior": "Comportamento", "enable_real_debrid": "Abilita Real Debrid", - "real_debrid_api_token_hint": "Puoi trovare la tua chiave API <0>here", + "debrid_api_token_hint": "Puoi trovare la tua chiave API <0>here", "save_changes": "Salva modifiche" }, "notifications": { diff --git a/src/locales/kk/translation.json b/src/locales/kk/translation.json index 6d5d84044..e66cd5ed0 100644 --- a/src/locales/kk/translation.json +++ b/src/locales/kk/translation.json @@ -159,13 +159,13 @@ "behavior": "Мінез-құлық", "download_sources": "Жүктеу көздері", "language": "Тіл", - "real_debrid_api_token": "API Кілті", + "api_token": "API Кілті", "enable_real_debrid": "Real-Debrid-ті қосу", "real_debrid_description": "Real-Debrid - бұл шектеусіз жүктеуші, ол интернетте орналастырылған файлдарды тез жүктеуге немесе жеке желі арқылы кез келген блоктарды айналып өтіп, оларды бірден плеерге беруге мүмкіндік береді.", - "real_debrid_invalid_token": "Қате API кілті", - "real_debrid_api_token_hint": "API кілтін <0>осы жерден алуға болады", + "debrid_invalid_token": "Қате API кілті", + "debrid_api_token_hint": "API кілтін <0>осы жерден алуға болады", "real_debrid_free_account_error": "\"{{username}}\" аккаунты жазылымға ие емес. Real-Debrid жазылымын алыңыз", - "real_debrid_linked_message": "\"{{username}}\" аккаунты байланған", + "debrid_linked_message": "\"{{username}}\" аккаунты байланған", "save_changes": "Өзгерістерді сақтау", "changes_saved": "Өзгерістер сәтті сақталды", "download_sources_description": "Hydra осы көздерден жүктеу сілтемелерін алады. URL-да жүктеу сілтемелері бар .json файлына тікелей сілтеме болуы керек.", diff --git a/src/locales/ko/translation.json b/src/locales/ko/translation.json index 2656bb452..bc8e60567 100644 --- a/src/locales/ko/translation.json +++ b/src/locales/ko/translation.json @@ -110,7 +110,7 @@ "general": "일반", "behavior": "행동", "enable_real_debrid": "Real-Debrid 활성화", - "real_debrid_api_token_hint": "API 키를 <0>이곳에서 얻으세요.", + "debrid_api_token_hint": "API 키를 <0>이곳에서 얻으세요.", "save_changes": "변경 사항 저장" }, "notifications": { diff --git a/src/locales/nb/translation.json b/src/locales/nb/translation.json index 5c5f68823..a6fb1bcc2 100644 --- a/src/locales/nb/translation.json +++ b/src/locales/nb/translation.json @@ -177,13 +177,13 @@ "behavior": "Oppførsel", "download_sources": "Nedlastingskilder", "language": "Språk", - "real_debrid_api_token": "API nøkkel", + "api_token": "API nøkkel", "enable_real_debrid": "Slå på Real-Debrid", "real_debrid_description": "Real-Debrid er en ubegrenset nedlaster som gør det mulig for deg å laste ned filer med en gang og med den beste utnyttelsen av internethastigheten din.", - "real_debrid_invalid_token": "Ugyldig API nøkkel", - "real_debrid_api_token_hint": "Du kan få API nøkkelen din <0>her", + "debrid_invalid_token": "Ugyldig API nøkkel", + "debrid_api_token_hint": "Du kan få API nøkkelen din <0>her", "real_debrid_free_account_error": "Brukeren \"{{username}}\" er en gratis bruker. Vennligst abboner på Real-Debrid", - "real_debrid_linked_message": "Brukeren \"{{username}}\" er forbunnet", + "debrid_linked_message": "Brukeren \"{{username}}\" er forbunnet", "save_changes": "Lagre endringer", "changes_saved": "Lagring av endringer vellykket", "download_sources_description": "Hydra vil hente nedlastingslenker fra disse kildene. Kilde URLen skal være en direkte lenke til en .json fil som inneholder nedlastingslenkene.", diff --git a/src/locales/nl/translation.json b/src/locales/nl/translation.json index bb7abf59c..6d9de5fa7 100644 --- a/src/locales/nl/translation.json +++ b/src/locales/nl/translation.json @@ -111,7 +111,7 @@ "general": "Algemeen", "behavior": "Gedrag", "enable_real_debrid": "Enable Real-Debrid", - "real_debrid_api_token_hint": "U kunt uw API-sleutel <0>hier verkrijgen.", + "debrid_api_token_hint": "U kunt uw API-sleutel <0>hier verkrijgen.", "save_changes": "Wijzigingen opslaan" }, "notifications": { diff --git a/src/locales/pl/translation.json b/src/locales/pl/translation.json index 945c857a0..bdaf822c2 100644 --- a/src/locales/pl/translation.json +++ b/src/locales/pl/translation.json @@ -119,7 +119,7 @@ "behavior": "Zachowania", "language": "Język", "enable_real_debrid": "Włącz Real-Debrid", - "real_debrid_api_token_hint": "Możesz uzyskać swój klucz API <0>tutaj", + "debrid_api_token_hint": "Możesz uzyskać swój klucz API <0>tutaj", "save_changes": "Zapisz zmiany" }, "notifications": { diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index af0747ee9..53fd48f59 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -225,13 +225,13 @@ "behavior": "Comportamento", "download_sources": "Fontes de download", "language": "Idioma", - "real_debrid_api_token": "Token de API", + "api_token": "Token de API", "enable_real_debrid": "Habilitar Real-Debrid", - "real_debrid_api_token_hint": "Você pode obter seu token de API <0>aqui", + "debrid_api_token_hint": "Você pode obter seu token de API <0>aqui", "real_debrid_description": "O Real-Debrid é um downloader sem restrições que permite baixar arquivos instantaneamente e com a melhor velocidade da sua Internet.", - "real_debrid_invalid_token": "Token de API inválido", + "debrid_invalid_token": "Token de API inválido", "real_debrid_free_account_error": "A conta \"{{username}}\" é uma conta gratuita. Por favor, assine a Real-Debrid", - "real_debrid_linked_message": "Conta \"{{username}}\" vinculada", + "debrid_linked_message": "Conta \"{{username}}\" vinculada", "save_changes": "Salvar mudanças", "changes_saved": "Ajustes salvos com sucesso", "download_sources_description": "Hydra vai buscar links de download em todas as fontes habilitadas. A URL da fonte deve ser um link direto para um arquivo .json contendo uma lista de links.", @@ -270,7 +270,6 @@ "disable_nsfw_alert": "Desativar alerta de conteúdo inapropriado", "seed_after_download_complete": "Semear após a conclusão do download", "show_hidden_achievement_description": "Mostrar descrição de conquistas ocultas antes de debloqueá-las", - "debrid_services": "Serviços Debrid", "account": "Conta", "no_users_blocked": "Você não bloqueou nenhum usuário", "subscription_active_until": "Sua assinatura Hydra Cloud ficará ativa até {{date}}", @@ -287,7 +286,8 @@ "subscription_renew_cancelled": "A renovação automática está desativada", "subscription_renews_on": "Sua assinatura renova dia {{date}}", "bill_sent_until": "Sua próxima cobrança será enviada até esse dia", - "enable_torbox": "Habilitar Torbox" + "enable_torbox": "Habilitar Torbox", + "torbox_description": "TorBox é o seu serviço de seedbox premium que rivaliza até com os melhores servidores do mercado." }, "notifications": { "download_complete": "Download concluído", diff --git a/src/locales/pt-PT/translation.json b/src/locales/pt-PT/translation.json index ce081b3f6..3b8496ae4 100644 --- a/src/locales/pt-PT/translation.json +++ b/src/locales/pt-PT/translation.json @@ -205,13 +205,13 @@ "behavior": "Comportamento", "download_sources": "Fontes de transferência", "language": "Idioma", - "real_debrid_api_token": "Token de API", + "api_token": "Token de API", "enable_real_debrid": "Ativar Real-Debrid", - "real_debrid_api_token_hint": "Podes obter o teu token de API <0>aqui", + "debrid_api_token_hint": "Podes obter o teu token de API <0>aqui", "real_debrid_description": "O Real-Debrid é um downloader sem restrições que permite descarregar ficheiros instantaneamente e com a melhor velocidade da tua Internet.", - "real_debrid_invalid_token": "Token de API inválido", + "debrid_invalid_token": "Token de API inválido", "real_debrid_free_account_error": "A conta \"{{username}}\" é uma conta gratuita. Por favor, subscreve o Real-Debrid", - "real_debrid_linked_message": "Conta \"{{username}}\" associada", + "debrid_linked_message": "Conta \"{{username}}\" associada", "save_changes": "Guardar alterações", "changes_saved": "Alterações guardadas com sucesso", "download_sources_description": "O Hydra vai procurar links de download em todas as fontes ativadas. O URL da fonte deve ser um link direto para um ficheiro .json que contenha uma lista de links.", diff --git a/src/locales/ro/translation.json b/src/locales/ro/translation.json index 69d140719..9003ecc68 100644 --- a/src/locales/ro/translation.json +++ b/src/locales/ro/translation.json @@ -124,13 +124,13 @@ "general": "General", "behavior": "Comportament", "language": "Limbă", - "real_debrid_api_token": "Token API", + "api_token": "Token API", "enable_real_debrid": "Activează Real-Debrid", "real_debrid_description": "Real-Debrid este un descărcător fără restricții care îți permite să descarci fișiere instantaneu și la cea mai bună viteză a internetului tău.", - "real_debrid_invalid_token": "Token API invalid", - "real_debrid_api_token_hint": "Poți obține token-ul tău API <0>aici", + "debrid_invalid_token": "Token API invalid", + "debrid_api_token_hint": "Poți obține token-ul tău API <0>aici", "real_debrid_free_account_error": "Contul \"{{username}}\" este un cont gratuit. Te rugăm să te abonezi la Real-Debrid", - "real_debrid_linked_message": "Contul \"{{username}}\" a fost legat", + "debrid_linked_message": "Contul \"{{username}}\" a fost legat", "save_changes": "Salvează modificările", "changes_saved": "Modificările au fost salvate cu succes" }, diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index 1b48c5e0d..18b8f1f54 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -237,13 +237,13 @@ "behavior": "Поведение", "download_sources": "Источники загрузки", "language": "Язык", - "real_debrid_api_token": "API Ключ", + "api_token": "API Ключ", "enable_real_debrid": "Включить Real-Debrid", "real_debrid_description": "Real-Debrid - это неограниченный загрузчик, который позволяет быстро скачивать файлы, размещенные в Интернете, или мгновенно передавать их в плеер через частную сеть, позволяющую обходить любые блокировки.", - "real_debrid_invalid_token": "Неверный API ключ", - "real_debrid_api_token_hint": "API ключ можно получить <0>здесь", + "debrid_invalid_token": "Неверный API ключ", + "debrid_api_token_hint": "API ключ можно получить <0>здесь", "real_debrid_free_account_error": "Аккаунт \"{{username}}\" - не имеет подписки. Пожалуйста, оформите подписку на Real-Debrid", - "real_debrid_linked_message": "Привязан аккаунт \"{{username}}\"", + "debrid_linked_message": "Привязан аккаунт \"{{username}}\"", "save_changes": "Сохранить изменения", "changes_saved": "Изменения успешно сохранены", "download_sources_description": "Hydra будет получать ссылки на загрузки из этих источников. URL должна содержать прямую ссылку на .json-файл с ссылками для загрузок.", diff --git a/src/locales/tr/translation.json b/src/locales/tr/translation.json index 6fa89c033..9095b693d 100644 --- a/src/locales/tr/translation.json +++ b/src/locales/tr/translation.json @@ -236,13 +236,13 @@ "behavior": "Davranış", "download_sources": "İndirme kaynakları", "language": "Dil", - "real_debrid_api_token": "API Anahtarı", + "api_token": "API Anahtarı", "enable_real_debrid": "Real-Debrid'i Etkinleştir", "real_debrid_description": "Real-Debrid, yalnızca internet hızınızla sınırlı olarak hızlı dosya indirmenizi sağlayan sınırsız bir indirici.", - "real_debrid_invalid_token": "Geçersiz API anahtarı", - "real_debrid_api_token_hint": "API anahtarınızı <0>buradan alabilirsiniz", + "debrid_invalid_token": "Geçersiz API anahtarı", + "debrid_api_token_hint": "API anahtarınızı <0>buradan alabilirsiniz", "real_debrid_free_account_error": "\"{{username}}\" hesabı ücretsiz bir hesaptır. Lütfen Real-Debrid abonesi olun", - "real_debrid_linked_message": "\"{{username}}\" hesabı bağlandı", + "debrid_linked_message": "\"{{username}}\" hesabı bağlandı", "save_changes": "Değişiklikleri Kaydet", "changes_saved": "Değişiklikler başarıyla kaydedildi", "download_sources_description": "Hydra, indirme bağlantılarını bu kaynaklardan alacak. Kaynak URL, indirme bağlantılarını içeren bir .json dosyasına doğrudan bir bağlantı olmalıdır.", diff --git a/src/locales/uk/translation.json b/src/locales/uk/translation.json index ed4b3d588..174e768cf 100644 --- a/src/locales/uk/translation.json +++ b/src/locales/uk/translation.json @@ -174,13 +174,13 @@ "import": "Імпортувати", "insert_valid_json_url": "Вставте дійсний URL JSON-файлу", "language": "Мова", - "real_debrid_api_token": "API-токен", - "real_debrid_api_token_hint": "API токен можливо отримати <0>тут", + "api_token": "API-токен", + "debrid_api_token_hint": "API токен можливо отримати <0>тут", "real_debrid_api_token_label": "Real-Debrid API-токен", "real_debrid_description": "Real-Debrid — це необмежений завантажувач, який дозволяє швидко завантажувати файли, розміщені в Інтернеті, або миттєво передавати їх у плеєр через приватну мережу, що дозволяє обходити будь-які блокування.", "real_debrid_free_account_error": "Акаунт \"{{username}}\" - не має наявної підписки. Будь ласка, оформіть підписку на Real-Debrid", - "real_debrid_invalid_token": "Невірний API-токен", - "real_debrid_linked_message": "Акаунт \"{{username}}\" привязаний", + "debrid_invalid_token": "Невірний API-токен", + "debrid_linked_message": "Акаунт \"{{username}}\" привязаний", "remove_download_source": "Видалити", "removed_download_source": "Джерело завантажень було видалено", "save_changes": "Зберегти зміни", diff --git a/src/locales/zh/translation.json b/src/locales/zh/translation.json index 664877fa8..ba48a21b8 100644 --- a/src/locales/zh/translation.json +++ b/src/locales/zh/translation.json @@ -213,13 +213,13 @@ "behavior": "行为", "download_sources": "下载源", "language": "语言", - "real_debrid_api_token": "API 令牌", + "api_token": "API 令牌", "enable_real_debrid": "启用 Real-Debrid", "real_debrid_description": "Real-Debrid 是一个无限制的下载器,允许您以最快的互联网速度即时下载文件。", - "real_debrid_invalid_token": "无效的 API 令牌", - "real_debrid_api_token_hint": "您可以从<0>这里获取API密钥.", + "debrid_invalid_token": "无效的 API 令牌", + "debrid_api_token_hint": "您可以从<0>这里获取API密钥.", "real_debrid_free_account_error": "账户 \"{{username}}\" 是免费账户。请订阅 Real-Debrid", - "real_debrid_linked_message": "账户 \"{{username}}\" 已链接", + "debrid_linked_message": "账户 \"{{username}}\" 已链接", "save_changes": "保存更改", "changes_saved": "更改已成功保存", "download_sources_description": "Hydra 将从这些源获取下载链接。源 URL 必须是直接链接到包含下载链接的 .json 文件。", diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 25882c3fc..570fa378c 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -46,6 +46,7 @@ import "./user-preferences/auto-launch"; import "./autoupdater/check-for-updates"; import "./autoupdater/restart-and-install-update"; import "./user-preferences/authenticate-real-debrid"; +import "./user-preferences/authenticate-torbox"; import "./download-sources/put-download-source"; import "./auth/sign-out"; import "./auth/open-auth-window"; diff --git a/src/main/events/user-preferences/authenticate-torbox.ts b/src/main/events/user-preferences/authenticate-torbox.ts new file mode 100644 index 000000000..87fc4ba82 --- /dev/null +++ b/src/main/events/user-preferences/authenticate-torbox.ts @@ -0,0 +1,14 @@ +import { registerEvent } from "../register-event"; +import { TorBoxClient } from "@main/services/download/torbox"; + +const authenticateTorBox = async ( + _event: Electron.IpcMainInvokeEvent, + apiToken: string +) => { + TorBoxClient.authorize(apiToken); + + const user = await TorBoxClient.getUser(); + return user; +}; + +registerEvent("authenticateTorBox", authenticateTorBox); diff --git a/src/preload/index.ts b/src/preload/index.ts index eac3c0a10..439327cd6 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -92,6 +92,8 @@ contextBridge.exposeInMainWorld("electron", { ipcRenderer.invoke("autoLaunch", autoLaunchProps), authenticateRealDebrid: (apiToken: string) => ipcRenderer.invoke("authenticateRealDebrid", apiToken), + authenticateTorBox: (apiToken: string) => + ipcRenderer.invoke("authenticateTorBox", apiToken), /* Download sources */ putDownloadSource: (objectIds: string[]) => diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index eaf5cb49d..c6db8649d 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -28,6 +28,7 @@ import type { CatalogueSearchPayload, LibraryGame, GameRunning, + TorBoxUser, } from "@types"; import type { AxiosProgressEvent } from "axios"; import type disk from "diskusage"; @@ -142,6 +143,7 @@ declare global { minimized: boolean; }) => Promise; authenticateRealDebrid: (apiToken: string) => Promise; + authenticateTorBox: (apiToken: string) => Promise; onAchievementUnlocked: (cb: () => void) => () => Electron.IpcRenderer; /* Download sources */ diff --git a/src/renderer/src/pages/settings/settings-debrid.css.ts b/src/renderer/src/pages/settings/settings-real-debrid.css.ts similarity index 100% rename from src/renderer/src/pages/settings/settings-debrid.css.ts rename to src/renderer/src/pages/settings/settings-real-debrid.css.ts diff --git a/src/renderer/src/pages/settings/settings-debrid.tsx b/src/renderer/src/pages/settings/settings-real-debrid.tsx similarity index 63% rename from src/renderer/src/pages/settings/settings-debrid.tsx rename to src/renderer/src/pages/settings/settings-real-debrid.tsx index ac6a09ffb..aa9562344 100644 --- a/src/renderer/src/pages/settings/settings-debrid.tsx +++ b/src/renderer/src/pages/settings/settings-real-debrid.tsx @@ -2,7 +2,7 @@ import { useContext, useEffect, useState } from "react"; import { Trans, useTranslation } from "react-i18next"; import { Button, CheckboxField, Link, TextField } from "@renderer/components"; -import * as styles from "./settings-debrid.css"; +import * as styles from "./settings-real-debrid.css"; import { useAppSelector, useToast } from "@renderer/hooks"; @@ -10,9 +10,8 @@ import { SPACING_UNIT } from "@renderer/theme.css"; import { settingsContext } from "@renderer/context"; const REAL_DEBRID_API_TOKEN_URL = "https://real-debrid.com/apitoken"; -const TORBOX_API_TOKEN_URL = "https://torbox.app/settings"; -export function SettingsDebrid() { +export function SettingsRealDebrid() { const userPreferences = useAppSelector( (state) => state.userPreferences.value ); @@ -23,8 +22,6 @@ export function SettingsDebrid() { const [form, setForm] = useState({ useRealDebrid: false, realDebridApiToken: null as string | null, - useTorBox: false, - torBoxApiToken: null as string | null, }); const { showSuccessToast, showErrorToast } = useToast(); @@ -36,8 +33,6 @@ export function SettingsDebrid() { setForm({ useRealDebrid: Boolean(userPreferences.realDebridApiToken), realDebridApiToken: userPreferences.realDebridApiToken ?? null, - useTorBox: Boolean(userPreferences.torBoxApiToken), - torBoxApiToken: userPreferences.torBoxApiToken ?? null, }); } }, [userPreferences]); @@ -62,7 +57,7 @@ export function SettingsDebrid() { return; } else { showSuccessToast( - t("real_debrid_linked_message", { username: user.username }) + t("debrid_linked_message", { username: user.username }) ); } } else { @@ -71,10 +66,9 @@ export function SettingsDebrid() { updateUserPreferences({ realDebridApiToken: form.useRealDebrid ? form.realDebridApiToken : null, - torBoxApiToken: form.useTorBox ? form.torBoxApiToken : null, }); } catch (err) { - showErrorToast(t("real_debrid_invalid_token")); + showErrorToast(t("debrid_invalid_token")); } finally { setIsLoading(false); } @@ -100,14 +94,18 @@ export function SettingsDebrid() { {form.useRealDebrid && ( setForm({ ...form, realDebridApiToken: event.target.value }) } placeholder="API Token" - containerProps={{ style: { marginTop: `${SPACING_UNIT}px` } }} + containerProps={{ + style: { + marginTop: `${SPACING_UNIT}px`, + }, + }} rightContent={ - } - hint={ - - - - } - /> - )} ); } diff --git a/src/renderer/src/pages/settings/settings-torbox.css.ts b/src/renderer/src/pages/settings/settings-torbox.css.ts new file mode 100644 index 000000000..0dfc9d789 --- /dev/null +++ b/src/renderer/src/pages/settings/settings-torbox.css.ts @@ -0,0 +1,13 @@ +import { style } from "@vanilla-extract/css"; + +import { SPACING_UNIT } from "../../theme.css"; + +export const form = style({ + display: "flex", + flexDirection: "column", + gap: `${SPACING_UNIT}px`, +}); + +export const description = style({ + marginBottom: `${SPACING_UNIT * 2}px`, +}); diff --git a/src/renderer/src/pages/settings/settings-torbox.tsx b/src/renderer/src/pages/settings/settings-torbox.tsx new file mode 100644 index 000000000..d289d9fd6 --- /dev/null +++ b/src/renderer/src/pages/settings/settings-torbox.tsx @@ -0,0 +1,119 @@ +import { useContext, useEffect, useState } from "react"; +import { Trans, useTranslation } from "react-i18next"; + +import { Button, CheckboxField, Link, TextField } from "@renderer/components"; +import * as styles from "./settings-torbox.css"; + +import { useAppSelector, useToast } from "@renderer/hooks"; + +import { SPACING_UNIT } from "@renderer/theme.css"; +import { settingsContext } from "@renderer/context"; + +const TORBOX_API_TOKEN_URL = "https://torbox.app/settings"; + +export function SettingsTorbox() { + const userPreferences = useAppSelector( + (state) => state.userPreferences.value + ); + + const { updateUserPreferences } = useContext(settingsContext); + + const [isLoading, setIsLoading] = useState(false); + const [form, setForm] = useState({ + useTorBox: false, + torBoxApiToken: null as string | null, + }); + + const { showSuccessToast, showErrorToast } = useToast(); + + const { t } = useTranslation("settings"); + + useEffect(() => { + if (userPreferences) { + setForm({ + useTorBox: Boolean(userPreferences.torBoxApiToken), + torBoxApiToken: userPreferences.torBoxApiToken ?? null, + }); + } + }, [userPreferences]); + + const handleFormSubmit: React.FormEventHandler = async ( + event + ) => { + setIsLoading(true); + event.preventDefault(); + + try { + if (form.useTorBox) { + const user = await window.electron.authenticateTorBox( + form.torBoxApiToken! + ); + + showSuccessToast(t("debrid_linked_message", { username: user.email })); + } else { + showSuccessToast(t("changes_saved")); + } + + updateUserPreferences({ + torBoxApiToken: form.useTorBox ? form.torBoxApiToken : null, + }); + } catch (err) { + showErrorToast(t("debrid_invalid_token")); + } finally { + setIsLoading(false); + } + }; + + const isButtonDisabled = + (form.useTorBox && !form.torBoxApiToken) || isLoading; + + return ( +
+

{t("torbox_description")}

+ + + setForm((prev) => ({ + ...prev, + useTorBox: !form.useTorBox, + })) + } + /> + + {form.useTorBox && ( + + setForm({ ...form, torBoxApiToken: event.target.value }) + } + placeholder="API Token" + containerProps={{ + style: { + marginTop: `${SPACING_UNIT}px`, + }, + }} + rightContent={ + + } + hint={ + + + + } + /> + )} + + ); +} diff --git a/src/renderer/src/pages/settings/settings.tsx b/src/renderer/src/pages/settings/settings.tsx index 702da53ea..9652f0aff 100644 --- a/src/renderer/src/pages/settings/settings.tsx +++ b/src/renderer/src/pages/settings/settings.tsx @@ -2,7 +2,7 @@ import { Button } from "@renderer/components"; import * as styles from "./settings.css"; import { useTranslation } from "react-i18next"; -import { SettingsDebrid } from "./settings-debrid"; +import { SettingsRealDebrid } from "./settings-real-debrid"; import { SettingsGeneral } from "./settings-general"; import { SettingsBehavior } from "./settings-behavior"; @@ -14,6 +14,7 @@ import { import { SettingsAccount } from "./settings-account"; import { useUserDetails } from "@renderer/hooks"; import { useMemo } from "react"; +import { SettingsTorbox } from "./settings-torbox"; export default function Settings() { const { t } = useTranslation("settings"); @@ -25,7 +26,8 @@ export default function Settings() { t("general"), t("behavior"), t("download_sources"), - t("debrid_services"), + "Torbox", + "Real-Debrid", ]; if (userDetails) return [...categories, t("account")]; @@ -50,7 +52,11 @@ export default function Settings() { } if (currentCategoryIndex === 3) { - return ; + return ; + } + + if (currentCategoryIndex === 4) { + return ; } return ; From bdf3a98c1aef9cdcbda8688e123cb084d70776eb Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sat, 1 Feb 2025 17:53:08 -0300 Subject: [PATCH 13/20] feat: add torbox icon --- src/main/events/cloud-save/upload-save-game.ts | 12 ++---------- src/main/services/process-watcher.ts | 5 ----- src/renderer/src/pages/settings/settings.tsx | 9 ++++++++- src/types/index.ts | 2 -- 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/main/events/cloud-save/upload-save-game.ts b/src/main/events/cloud-save/upload-save-game.ts index 48a60b5b8..7f1849091 100644 --- a/src/main/events/cloud-save/upload-save-game.ts +++ b/src/main/events/cloud-save/upload-save-game.ts @@ -40,7 +40,8 @@ const bundleBackup = async ( return tarLocation; }; -export const createBackup = async ( +export const uploadSaveGame = async ( + _event: Electron.IpcMainInvokeEvent, objectId: string, shop: GameShop, downloadOptionTitle: string | null @@ -102,13 +103,4 @@ export const createBackup = async ( }); }; -const uploadSaveGame = async ( - _event: Electron.IpcMainInvokeEvent, - objectId: string, - shop: GameShop, - downloadOptionTitle: string | null -) => { - return createBackup(objectId, shop, downloadOptionTitle); -}; - registerEvent("uploadSaveGame", uploadSaveGame); diff --git a/src/main/services/process-watcher.ts b/src/main/services/process-watcher.ts index 0cf4605f7..0b04defe7 100644 --- a/src/main/services/process-watcher.ts +++ b/src/main/services/process-watcher.ts @@ -6,7 +6,6 @@ import axios from "axios"; import { exec } from "child_process"; import { ProcessPayload } from "./download/types"; import { gamesSublevel, levelKeys } from "@main/level"; -import { createBackup } from "@main/events/cloud-save/upload-save-game"; const commands = { findWineDir: `lsof -c wine 2>/dev/null | grep '/drive_c/windows$' | head -n 1 | awk '{for(i=9;i<=NF;i++) printf "%s ", $i; print ""}'`, @@ -283,10 +282,6 @@ const onCloseGame = (game: Game) => { gamesPlaytime.delete(levelKeys.game(game.shop, game.objectId)); if (game.remoteId) { - // create backup - // todo: check for hydra cloud? - createBackup(game.objectId, game.shop, ""); - updateGamePlaytime( game, performance.now() - gamePlaytime.lastSyncTick, diff --git a/src/renderer/src/pages/settings/settings.tsx b/src/renderer/src/pages/settings/settings.tsx index 9652f0aff..c752890aa 100644 --- a/src/renderer/src/pages/settings/settings.tsx +++ b/src/renderer/src/pages/settings/settings.tsx @@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next"; import { SettingsRealDebrid } from "./settings-real-debrid"; import { SettingsGeneral } from "./settings-general"; import { SettingsBehavior } from "./settings-behavior"; - +import torBoxLogo from "@renderer/assets/icons/torbox.webp"; import { SettingsDownloadSources } from "./settings-download-sources"; import { SettingsContextConsumer, @@ -74,6 +74,13 @@ export default function Settings() { } onClick={() => setCurrentCategoryIndex(index)} > + {index === 3 && ( + TorBox + )} {category} ))} diff --git a/src/types/index.ts b/src/types/index.ts index 1f189a8da..1e089d086 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -10,8 +10,6 @@ export type HydraCloudFeature = | "backup" | "achievements-points"; -export type DebridServices = "RealDebrid" | "TorBox"; - export interface GameRepack { id: number; title: string; From 0820e4632bf30dde78a3b0b1d959d15aae14196b Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sat, 1 Feb 2025 18:21:17 -0300 Subject: [PATCH 14/20] feat: remove duplicate code --- src/types/torbox.types.ts | 78 --------------------------------------- 1 file changed, 78 deletions(-) delete mode 100644 src/types/torbox.types.ts diff --git a/src/types/torbox.types.ts b/src/types/torbox.types.ts deleted file mode 100644 index ee72600aa..000000000 --- a/src/types/torbox.types.ts +++ /dev/null @@ -1,78 +0,0 @@ -export interface TorBoxUser { - id: number; - email: string; - plan: string; - expiration: string; -} - -export interface TorBoxUserRequest { - success: boolean; - detail: string; - error: string; - data: TorBoxUser; -} - -export interface TorBoxFile { - id: number; - md5: string; - s3_path: string; - name: string; - size: number; - mimetype: string; - short_name: string; -} - -export interface TorBoxTorrentInfo { - id: number; - hash: string; - created_at: string; - updated_at: string; - magnet: string; - size: number; - active: boolean; - cached: boolean; - auth_id: string; - download_state: - | "downloading" - | "uploading" - | "stalled (no seeds)" - | "paused" - | "completed" - | "cached" - | "metaDL" - | "checkingResumeData"; - seeds: number; - ratio: number; - progress: number; - download_speed: number; - upload_speed: number; - name: string; - eta: number; - files: TorBoxFile[]; -} - -export interface TorBoxTorrentInfoRequest { - success: boolean; - detail: string; - error: string | null; - data: TorBoxTorrentInfo[]; -} - -export interface TorBoxAddTorrentRequest { - success: boolean; - detail: string; - error: string | null; - data: { - torrent_id: number; - name: string; - hash: string; - size: number; - }; -} - -export interface TorBoxRequestLinkRequest { - success: boolean; - detail: string; - error: string; - data: string; -} From df86a7716b92ce1840ff2e5353dda12c7efabaa7 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sat, 1 Feb 2025 18:39:08 -0300 Subject: [PATCH 15/20] feat: remove out name pixel drain --- src/main/events/cloud-save/upload-save-game.ts | 2 +- src/main/services/download/download-manager.ts | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main/events/cloud-save/upload-save-game.ts b/src/main/events/cloud-save/upload-save-game.ts index 7f1849091..0c18d0a67 100644 --- a/src/main/events/cloud-save/upload-save-game.ts +++ b/src/main/events/cloud-save/upload-save-game.ts @@ -40,7 +40,7 @@ const bundleBackup = async ( return tarLocation; }; -export const uploadSaveGame = async ( +const uploadSaveGame = async ( _event: Electron.IpcMainInvokeEvent, objectId: string, shop: GameShop, diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts index c380638d1..d109663ee 100644 --- a/src/main/services/download/download-manager.ts +++ b/src/main/services/download/download-manager.ts @@ -16,7 +16,6 @@ import { logger } from "../logger"; import { db, downloadsSublevel, gamesSublevel, levelKeys } from "@main/level"; import { sortBy } from "lodash-es"; import { TorBoxClient } from "./torbox"; -import axios from "axios"; export class DownloadManager { private static downloadingGameId: string | null = null; @@ -276,16 +275,11 @@ export class DownloadManager { case Downloader.PixelDrain: { const id = download.uri.split("/").pop(); - const name = await axios - .get(`https://pixeldrain.com/api/file/${id}/info`) - .then((res) => res.data.name as string); - return { action: "start", game_id: downloadId, url: `https://cdn.pd5-gamedriveorg.workers.dev/api/file/${id}`, save_path: download.downloadPath, - out: name, }; } case Downloader.Qiwi: { From 10f23bd997e58bc9743e82253e856f71ccc59835 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sat, 1 Feb 2025 19:51:47 -0300 Subject: [PATCH 16/20] feat: use correct label --- src/renderer/src/components/text-field/text-field.css.ts | 2 +- src/renderer/src/pages/settings/settings-real-debrid.tsx | 2 +- src/renderer/src/pages/settings/settings-torbox.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/renderer/src/components/text-field/text-field.css.ts b/src/renderer/src/components/text-field/text-field.css.ts index f983508ee..8c993993c 100644 --- a/src/renderer/src/components/text-field/text-field.css.ts +++ b/src/renderer/src/components/text-field/text-field.css.ts @@ -14,7 +14,7 @@ export const textField = recipe({ base: { display: "inline-flex", transition: "all ease 0.2s", - width: "100%", + flex: 1, alignItems: "center", borderRadius: "8px", border: `solid 1px ${vars.color.border}`, diff --git a/src/renderer/src/pages/settings/settings-real-debrid.tsx b/src/renderer/src/pages/settings/settings-real-debrid.tsx index aa9562344..dd4ecef46 100644 --- a/src/renderer/src/pages/settings/settings-real-debrid.tsx +++ b/src/renderer/src/pages/settings/settings-real-debrid.tsx @@ -114,7 +114,7 @@ export function SettingsRealDebrid() { }} disabled={isButtonDisabled} > - {t("save")} + {t("save_changes")} } hint={ diff --git a/src/renderer/src/pages/settings/settings-torbox.tsx b/src/renderer/src/pages/settings/settings-torbox.tsx index d289d9fd6..52682ccc5 100644 --- a/src/renderer/src/pages/settings/settings-torbox.tsx +++ b/src/renderer/src/pages/settings/settings-torbox.tsx @@ -104,7 +104,7 @@ export function SettingsTorbox() { }} disabled={isButtonDisabled} > - {t("save")} + {t("save_changes")} } hint={ From 8dba75df34626051a5ba559bb715fb7356fb831e Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sat, 1 Feb 2025 20:18:10 -0300 Subject: [PATCH 17/20] feat: add title to toast --- src/locales/en/translation.json | 4 +++- src/locales/pt-BR/translation.json | 4 +++- src/renderer/src/pages/settings/settings-real-debrid.tsx | 1 + src/renderer/src/pages/settings/settings-torbox.tsx | 5 ++++- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index a1914c58a..704f82a7e 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -298,7 +298,9 @@ "subscription_renews_on": "Your subscription renews on {{date}}", "bill_sent_until": "Your next bill will be sent until this day", "enable_torbox": "Enable Torbox", - "torbox_description": "TorBox is your premium seedbox service rivaling even the best servers on the market." + "torbox_description": "TorBox is your premium seedbox service rivaling even the best servers on the market.", + "torbox_account_linked": "TorBox account linked", + "real_debrid_account_linked": "Real-Debrid account linked" }, "notifications": { "download_complete": "Download complete", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 53fd48f59..403754db9 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -287,7 +287,9 @@ "subscription_renews_on": "Sua assinatura renova dia {{date}}", "bill_sent_until": "Sua próxima cobrança será enviada até esse dia", "enable_torbox": "Habilitar Torbox", - "torbox_description": "TorBox é o seu serviço de seedbox premium que rivaliza até com os melhores servidores do mercado." + "torbox_description": "TorBox é o seu serviço de seedbox premium que rivaliza até com os melhores servidores do mercado.", + "torbox_account_linked": "Conta do TorBox vinculada", + "real_debrid_account_linked": "Conta Real-Debrid associada" }, "notifications": { "download_complete": "Download concluído", diff --git a/src/renderer/src/pages/settings/settings-real-debrid.tsx b/src/renderer/src/pages/settings/settings-real-debrid.tsx index dd4ecef46..300acc8e0 100644 --- a/src/renderer/src/pages/settings/settings-real-debrid.tsx +++ b/src/renderer/src/pages/settings/settings-real-debrid.tsx @@ -57,6 +57,7 @@ export function SettingsRealDebrid() { return; } else { showSuccessToast( + t("real_debrid_account_linked"), t("debrid_linked_message", { username: user.username }) ); } diff --git a/src/renderer/src/pages/settings/settings-torbox.tsx b/src/renderer/src/pages/settings/settings-torbox.tsx index 52682ccc5..fa7a41f5c 100644 --- a/src/renderer/src/pages/settings/settings-torbox.tsx +++ b/src/renderer/src/pages/settings/settings-torbox.tsx @@ -49,7 +49,10 @@ export function SettingsTorbox() { form.torBoxApiToken! ); - showSuccessToast(t("debrid_linked_message", { username: user.email })); + showSuccessToast( + t("torbox_account_linked"), + t("debrid_linked_message", { username: user.email }) + ); } else { showSuccessToast(t("changes_saved")); } From 220b3620d4aa09b7a4e1e4a5bfce45fe6a6aaf46 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sat, 1 Feb 2025 21:09:06 -0300 Subject: [PATCH 18/20] feat: add torbox error handling --- src/main/events/torrenting/start-game-download.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/events/torrenting/start-game-download.ts b/src/main/events/torrenting/start-game-download.ts index 96e3499a9..8b5f19180 100644 --- a/src/main/events/torrenting/start-game-download.ts +++ b/src/main/events/torrenting/start-game-download.ts @@ -76,10 +76,10 @@ const startGameDownload = async ( queued: true, }; - await downloadsSublevel.put(gameKey, download); - try { - await DownloadManager.startDownload(download); + await DownloadManager.startDownload(download).then(() => { + return downloadsSublevel.put(gameKey, download); + }); const updatedGame = await gamesSublevel.get(gameKey); @@ -113,6 +113,10 @@ const startGameDownload = async ( error: DownloadError.RealDebridAccountNotAuthorized, }; } + + if (downloader === Downloader.TorBox) { + return { ok: false, error: err.response?.data?.detail }; + } } if (err instanceof Error) { From ba6d8dd6a48001823b581ee399424b19f515af6e Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sat, 1 Feb 2025 21:12:15 -0300 Subject: [PATCH 19/20] feat: remove unnecessary inline css --- src/renderer/src/pages/settings/settings-real-debrid.tsx | 8 +------- src/renderer/src/pages/settings/settings-torbox.tsx | 8 +------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/renderer/src/pages/settings/settings-real-debrid.tsx b/src/renderer/src/pages/settings/settings-real-debrid.tsx index 300acc8e0..0d4e51080 100644 --- a/src/renderer/src/pages/settings/settings-real-debrid.tsx +++ b/src/renderer/src/pages/settings/settings-real-debrid.tsx @@ -108,13 +108,7 @@ export function SettingsRealDebrid() { }, }} rightContent={ - } diff --git a/src/renderer/src/pages/settings/settings-torbox.tsx b/src/renderer/src/pages/settings/settings-torbox.tsx index fa7a41f5c..ce4b5fcca 100644 --- a/src/renderer/src/pages/settings/settings-torbox.tsx +++ b/src/renderer/src/pages/settings/settings-torbox.tsx @@ -100,13 +100,7 @@ export function SettingsTorbox() { }, }} rightContent={ - } From 4e2427dbefdd97b922f2cd00765ddac74b2173fc Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sat, 1 Feb 2025 21:23:22 -0300 Subject: [PATCH 20/20] feat: refactor settings tabs --- src/renderer/src/pages/settings/settings.tsx | 37 +++++++++++--------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/renderer/src/pages/settings/settings.tsx b/src/renderer/src/pages/settings/settings.tsx index c752890aa..a3a955e20 100644 --- a/src/renderer/src/pages/settings/settings.tsx +++ b/src/renderer/src/pages/settings/settings.tsx @@ -23,14 +23,26 @@ export default function Settings() { const categories = useMemo(() => { const categories = [ - t("general"), - t("behavior"), - t("download_sources"), - "Torbox", - "Real-Debrid", + { tabLabel: t("general"), contentTitle: t("general") }, + { tabLabel: t("behavior"), contentTitle: t("behavior") }, + { tabLabel: t("download_sources"), contentTitle: t("download_sources") }, + { + tabLabel: ( + <> + TorBox + Torbox + + ), + contentTitle: "TorBox", + }, + { tabLabel: "Real-Debrid", contentTitle: "Real-Debrid" }, ]; - if (userDetails) return [...categories, t("account")]; + if (userDetails) + return [ + ...categories, + { tabLabel: t("account"), contentTitle: t("account") }, + ]; return categories; }, [userDetails, t]); @@ -68,25 +80,18 @@ export default function Settings() {
{categories.map((category, index) => ( ))}
-

{categories[currentCategoryIndex]}

+

{categories[currentCategoryIndex].contentTitle}

{renderCategory()}