From 21ca26dcfee4fe4fb846068057241ac348a934c6 Mon Sep 17 00:00:00 2001 From: Hachi-R Date: Fri, 8 Nov 2024 17:30:18 -0300 Subject: [PATCH 01/28] feat: update schema with seeding columns --- src/main/entity/game.entity.ts | 3 +++ src/main/entity/user-preferences.entity.ts | 3 +++ src/main/knex-client.ts | 4 ++++ .../20241108200154_add_should_seed_colum.ts | 17 ++++++++++++++++ .../20241108201806_add_seed_after_download.ts | 20 +++++++++++++++++++ src/types/index.ts | 2 ++ 6 files changed, 49 insertions(+) create mode 100644 src/main/migrations/20241108200154_add_should_seed_colum.ts create mode 100644 src/main/migrations/20241108201806_add_seed_after_download.ts diff --git a/src/main/entity/game.entity.ts b/src/main/entity/game.entity.ts index 19905c322..baa6355c4 100644 --- a/src/main/entity/game.entity.ts +++ b/src/main/entity/game.entity.ts @@ -85,6 +85,9 @@ export class Game { @Column("boolean", { default: false }) isDeleted: boolean; + @Column("boolean", { default: false }) + shouldSeed: boolean; + @CreateDateColumn() createdAt: Date; diff --git a/src/main/entity/user-preferences.entity.ts b/src/main/entity/user-preferences.entity.ts index 357dfb501..24cfcc8f4 100644 --- a/src/main/entity/user-preferences.entity.ts +++ b/src/main/entity/user-preferences.entity.ts @@ -41,6 +41,9 @@ export class UserPreferences { @Column("boolean", { default: false }) disableNsfwAlert: boolean; + @Column("boolean", { default: true }) + seedAfterDownloadComplete: boolean; + @CreateDateColumn() createdAt: Date; diff --git a/src/main/knex-client.ts b/src/main/knex-client.ts index 988d42dad..188fa2a5e 100644 --- a/src/main/knex-client.ts +++ b/src/main/knex-client.ts @@ -13,6 +13,8 @@ import { AddBackgroundImageUrl } from "./migrations/20241016100249_add_backgroun import { AddWinePrefixToGame } from "./migrations/20241019081648_add_wine_prefix_to_game"; import { AddStartMinimizedColumn } from "./migrations/20241030171454_add_start_minimized_column"; import { AddDisableNsfwAlertColumn } from "./migrations/20241106053733_add_disable_nsfw_alert_column"; +import { AddShouldSeedColumn } from "./migrations/20241108200154_add_should_seed_colum"; +import { AddSeedAfterDownloadColumn } from "./migrations/20241108201806_add_seed_after_download"; export type HydraMigration = Knex.Migration & { name: string }; class MigrationSource implements Knex.MigrationSource { @@ -30,6 +32,8 @@ class MigrationSource implements Knex.MigrationSource { AddWinePrefixToGame, AddStartMinimizedColumn, AddDisableNsfwAlertColumn, + AddShouldSeedColumn, + AddSeedAfterDownloadColumn, ]); } getMigrationName(migration: HydraMigration): string { diff --git a/src/main/migrations/20241108200154_add_should_seed_colum.ts b/src/main/migrations/20241108200154_add_should_seed_colum.ts new file mode 100644 index 000000000..60be07305 --- /dev/null +++ b/src/main/migrations/20241108200154_add_should_seed_colum.ts @@ -0,0 +1,17 @@ +import type { HydraMigration } from "@main/knex-client"; +import type { Knex } from "knex"; + +export const AddShouldSeedColumn: HydraMigration = { + name: "AddShouldSeedColumn", + up: (knex: Knex) => { + return knex.schema.alterTable("game", (table) => { + return table.boolean("shouldSeed").notNullable().defaultTo(false); + }); + }, + + down: async (knex: Knex) => { + return knex.schema.alterTable("game", (table) => { + return table.dropColumn("shouldSeed"); + }); + }, +}; diff --git a/src/main/migrations/20241108201806_add_seed_after_download.ts b/src/main/migrations/20241108201806_add_seed_after_download.ts new file mode 100644 index 000000000..75b94577a --- /dev/null +++ b/src/main/migrations/20241108201806_add_seed_after_download.ts @@ -0,0 +1,20 @@ +import type { HydraMigration } from "@main/knex-client"; +import type { Knex } from "knex"; + +export const AddSeedAfterDownloadColumn: HydraMigration = { + name: "AddSeedAfterDownloadColumn", + up: (knex: Knex) => { + return knex.schema.alterTable("user_preferences", (table) => { + return table + .boolean("seedAfterDownloadComplete") + .notNullable() + .defaultTo(true); + }); + }, + + down: async (knex: Knex) => { + return knex.schema.alterTable("user_preferences", (table) => { + return table.dropColumn("seedAfterDownloadComplete"); + }); + }, +}; diff --git a/src/types/index.ts b/src/types/index.ts index c0269cd3a..9116a9984 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -124,6 +124,7 @@ export interface Game { objectID: string; shop: GameShop; downloadQueue: DownloadQueue | null; + shouldSeed: boolean; createdAt: Date; updatedAt: Date; } @@ -162,6 +163,7 @@ export interface UserPreferences { runAtStartup: boolean; startMinimized: boolean; disableNsfwAlert: boolean; + seedAfterDownloadComplete: boolean; } export interface Steam250Game { From 9c9c0e6c09bfb316da521b1d4e2798490f7d4285 Mon Sep 17 00:00:00 2001 From: Hachi-R Date: Fri, 8 Nov 2024 18:24:06 -0300 Subject: [PATCH 02/28] feat: seed after doenload --- torrent-client/torrent_downloader.py | 30 ++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/torrent-client/torrent_downloader.py b/torrent-client/torrent_downloader.py index b52802607..a098f7dbd 100644 --- a/torrent-client/torrent_downloader.py +++ b/torrent-client/torrent_downloader.py @@ -158,8 +158,34 @@ def get_download_status(self): } if status.progress == 1: - torrent_handle.pause() - self.session.remove_torrent(torrent_handle) self.downloading_game_id = -1 return response + + def get_seed_status(self): + response = [] + + for game_id, torrent_handle in self.torrent_handles.items(): + if game_id == self.downloading_game_id: + continue + + status = torrent_handle.status() + info = torrent_handle.torrent_file() + + torrent_info = { + 'folderName': info.name() if info else "", + 'fileSize': info.total_size() if info else 0, + 'gameId': game_id, + 'progress': status.progress, + 'downloadSpeed': status.download_rate, + 'uploadSpeed': status.upload_rate, + 'numPeers': status.num_peers, + 'numSeeds': status.num_seeds, + 'status': status.state, + 'bytesDownloaded': status.progress * info.total_size() if info else status.all_time_download, + } + + if status.state == 5: + response.append(torrent_info) + + return response From c556a00e4a324aff6f0c83252b7abfe1f5b9d0ae Mon Sep 17 00:00:00 2001 From: Hachi-R Date: Fri, 8 Nov 2024 18:28:58 -0300 Subject: [PATCH 03/28] feat: get seed status --- src/main/services/download/python-instance.ts | 10 ++++++++++ src/main/services/download/types.ts | 1 + src/main/services/main-loop.ts | 1 + torrent-client/main.py | 14 ++++++++++++++ 4 files changed, 26 insertions(+) diff --git a/src/main/services/download/python-instance.ts b/src/main/services/download/python-instance.ts index f59b20b8d..09ea10edc 100644 --- a/src/main/services/download/python-instance.ts +++ b/src/main/services/download/python-instance.ts @@ -123,6 +123,16 @@ export class PythonInstance { } } + public static async getSeedStatus() { + const response = await this.rpc.get( + "/seed-status" + ); + + if (response.data === null) return []; + + return response.data; + } + static async pauseDownload() { await this.rpc .post("/action", { diff --git a/src/main/services/download/types.ts b/src/main/services/download/types.ts index fd8009a2a..c09455c70 100644 --- a/src/main/services/download/types.ts +++ b/src/main/services/download/types.ts @@ -25,6 +25,7 @@ export interface LibtorrentPayload { numPeers: number; numSeeds: number; downloadSpeed: number; + uploadSpeed: number; bytesDownloaded: number; fileSize: number; folderName: string; diff --git a/src/main/services/main-loop.ts b/src/main/services/main-loop.ts index b4836b46b..a1c2b449b 100644 --- a/src/main/services/main-loop.ts +++ b/src/main/services/main-loop.ts @@ -10,6 +10,7 @@ export const startMainLoop = async () => { watchProcesses(), DownloadManager.watchDownloads(), AchievementWatcherManager.watchAchievements(), + DownloadManager.getSeedStatus(), ]); await sleep(1500); diff --git a/torrent-client/main.py b/torrent-client/main.py index d62150b8f..c8ffd9e22 100644 --- a/torrent-client/main.py +++ b/torrent-client/main.py @@ -50,6 +50,20 @@ def do_GET(self): self.wfile.write(json.dumps(status).encode('utf-8')) + elif self.path == "/seed-status": + if self.headers.get(self.rpc_password_header) != rpc_password: + self.send_response(401) + self.end_headers() + return + + self.send_response(200) + self.send_header("Content-type", "application/json") + self.end_headers() + + status = torrent_downloader.get_seed_status() + + self.wfile.write(json.dumps(status).encode('utf-8')) + elif self.path == "/healthcheck": self.send_response(200) self.end_headers() From b32952f0764cfc2b6d9dbc63cf7653f3442dcad5 Mon Sep 17 00:00:00 2001 From: Hachi-R Date: Fri, 8 Nov 2024 21:29:40 -0300 Subject: [PATCH 04/28] feat: add ablity to pause and resume the seeding process --- src/main/events/index.ts | 2 ++ src/main/events/torrenting/pause-game-seed.ts | 21 +++++++++++++ .../events/torrenting/resume-game-seed.ts | 31 +++++++++++++++++++ src/main/services/download/python-instance.ts | 22 +++++++++++++ src/main/services/download/types.ts | 8 +++++ src/preload/index.ts | 4 +++ src/renderer/src/declaration.d.ts | 2 ++ src/renderer/src/hooks/use-download.ts | 12 +++++++ src/types/index.ts | 1 + torrent-client/main.py | 4 +++ torrent-client/torrent_downloader.py | 13 ++++++++ 11 files changed, 120 insertions(+) create mode 100644 src/main/events/torrenting/pause-game-seed.ts create mode 100644 src/main/events/torrenting/resume-game-seed.ts diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 932b80e42..4a5175e3f 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -32,6 +32,8 @@ import "./torrenting/cancel-game-download"; import "./torrenting/pause-game-download"; import "./torrenting/resume-game-download"; import "./torrenting/start-game-download"; +import "./torrenting/pause-game-seed"; +import "./torrenting/resume-game-seed"; import "./user-preferences/get-user-preferences"; import "./user-preferences/update-user-preferences"; import "./user-preferences/auto-launch"; diff --git a/src/main/events/torrenting/pause-game-seed.ts b/src/main/events/torrenting/pause-game-seed.ts new file mode 100644 index 000000000..3b00ddd50 --- /dev/null +++ b/src/main/events/torrenting/pause-game-seed.ts @@ -0,0 +1,21 @@ +import { registerEvent } from "../register-event"; + +import { DownloadManager } from "@main/services"; +import { dataSource } from "@main/data-source"; +import { Game } from "@main/entity"; + +const pauseGameSeed = async ( + _event: Electron.IpcMainInvokeEvent, + gameId: number +) => { + await dataSource.transaction(async (transactionalEntityManager) => { + + await transactionalEntityManager + .getRepository(Game) + .update({ id: gameId }, { status: "complete", shouldSeed: false }); + }); + + await DownloadManager.pauseSeeding(gameId); +}; + +registerEvent("pauseGameSeed", pauseGameSeed); diff --git a/src/main/events/torrenting/resume-game-seed.ts b/src/main/events/torrenting/resume-game-seed.ts new file mode 100644 index 000000000..133b26a03 --- /dev/null +++ b/src/main/events/torrenting/resume-game-seed.ts @@ -0,0 +1,31 @@ +import { registerEvent } from "../register-event"; +import { gameRepository } from "../../repository"; + +import { DownloadManager } from "@main/services"; +import { dataSource } from "@main/data-source"; +import { Game } from "@main/entity"; + +const resumeGameSeed = async ( + _event: Electron.IpcMainInvokeEvent, + gameId: number +) => { + const game = await gameRepository.findOne({ + where: { + id: gameId, + isDeleted: false, + }, + }); + + if (!game) return; + + await dataSource.transaction(async (transactionalEntityManager) => { + + await transactionalEntityManager + .getRepository(Game) + .update({ id: gameId }, { status: "seeding", shouldSeed: true }); + }); + + await DownloadManager.resumeSeeding(gameId, game.uri!, game.downloadPath!); +}; + +registerEvent("resumeGameSeed", resumeGameSeed); diff --git a/src/main/services/download/python-instance.ts b/src/main/services/download/python-instance.ts index 09ea10edc..95997c4cd 100644 --- a/src/main/services/download/python-instance.ts +++ b/src/main/services/download/python-instance.ts @@ -18,6 +18,8 @@ import { LibtorrentStatus, LibtorrentPayload, ProcessPayload, + PauseSeedingPayload, + ResumeSeedingPayload, } from "./types"; import { pythonInstanceLogger as logger } from "../logger"; @@ -133,6 +135,26 @@ export class PythonInstance { return response.data; } + static async pauseSeeding(gameId: number) { + await this.rpc + .post("/action", { + action: "pause-seeding", + game_id: gameId, + } as PauseSeedingPayload) + .catch(() => {}); + } + + static async resumeSeeding(gameId: number, magnet: string, savePath: string) { + await this.rpc + .post("/action", { + action: "resume-seeding", + game_id: gameId, + magnet, + save_path: savePath, + } as ResumeSeedingPayload) + .catch(() => {}); + } + static async pauseDownload() { await this.rpc .post("/action", { diff --git a/src/main/services/download/types.ts b/src/main/services/download/types.ts index c09455c70..2d9927350 100644 --- a/src/main/services/download/types.ts +++ b/src/main/services/download/types.ts @@ -37,3 +37,11 @@ export interface ProcessPayload { exe: string; pid: number; } + +export interface PauseSeedingPayload { + game_id: number; +} + +export interface ResumeSeedingPayload { + game_id: number; +} diff --git a/src/preload/index.ts b/src/preload/index.ts index d8d142ca6..f1a26150f 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -26,6 +26,10 @@ contextBridge.exposeInMainWorld("electron", { ipcRenderer.invoke("pauseGameDownload", gameId), resumeGameDownload: (gameId: number) => ipcRenderer.invoke("resumeGameDownload", gameId), + pauseGameSeed: (gameId: number) => + ipcRenderer.invoke("pauseGameSeed", gameId), + resumeGameSeed: (gameId: number) => + ipcRenderer.invoke("resumeGameSeed", gameId), onDownloadProgress: (cb: (value: DownloadProgress) => void) => { const listener = ( _event: Electron.IpcRendererEvent, diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 4065b64c8..93e5b2958 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -46,6 +46,8 @@ declare global { cancelGameDownload: (gameId: number) => Promise; pauseGameDownload: (gameId: number) => Promise; resumeGameDownload: (gameId: number) => Promise; + pauseGameSeed: (gameId: number) => Promise; + resumeGameSeed: (gameId: number) => Promise; onDownloadProgress: ( cb: (value: DownloadProgress) => void ) => () => Electron.IpcRenderer; diff --git a/src/renderer/src/hooks/use-download.ts b/src/renderer/src/hooks/use-download.ts index 31c3bf2f0..4ea79b934 100644 --- a/src/renderer/src/hooks/use-download.ts +++ b/src/renderer/src/hooks/use-download.ts @@ -66,6 +66,16 @@ export function useDownload() { updateLibrary(); }); + const pauseSeeding = async (gameId: number) => { + await window.electron.pauseGameSeed(gameId); + await updateLibrary(); + }; + + const resumeSeeding = async (gameId: number) => { + await window.electron.resumeGameSeed(gameId); + await updateLibrary(); + }; + const calculateETA = () => { if (!lastPacket || lastPacket.timeRemaining < 0) return ""; @@ -96,6 +106,8 @@ export function useDownload() { removeGameFromLibrary, removeGameInstaller, isGameDeleting, + pauseSeeding, + resumeSeeding, clearDownload: () => dispatch(clearDownload()), setLastPacket: (packet: DownloadProgress) => dispatch(setLastPacket(packet)), diff --git a/src/types/index.ts b/src/types/index.ts index 9116a9984..2e4894b37 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -7,6 +7,7 @@ export type GameStatus = | "paused" | "error" | "complete" + | "seeding" | "removed"; export type GameShop = "steam" | "epic"; diff --git a/torrent-client/main.py b/torrent-client/main.py index c8ffd9e22..a37ffabc6 100644 --- a/torrent-client/main.py +++ b/torrent-client/main.py @@ -121,6 +121,10 @@ def do_POST(self): elif data['action'] == 'kill-torrent': torrent_downloader.abort_session() torrent_downloader = None + elif data['action'] == 'pause-seeding': + torrent_downloader.pause_seeding(data['game_id']) + elif data['action'] == 'resume-seeding': + torrent_downloader.resume_seeding(data['game_id'], data['magnet'], data['save_path']) self.send_response(200) self.end_headers() diff --git a/torrent-client/torrent_downloader.py b/torrent-client/torrent_downloader.py index a098f7dbd..41c452782 100644 --- a/torrent-client/torrent_downloader.py +++ b/torrent-client/torrent_downloader.py @@ -189,3 +189,16 @@ def get_seed_status(self): response.append(torrent_info) return response + + def pause_seeding(self, game_id: int): + torrent_handle = self.torrent_handles.get(game_id) + if torrent_handle: + torrent_handle.pause() + torrent_handle.unset_flags(lt.torrent_flags.auto_managed) + + def resume_seeding(self, game_id: int, magnet: str, save_path: str): + params = {'url': magnet, 'save_path': save_path, 'trackers': self.trackers} + torrent_handle = self.session.add_torrent(params) + self.torrent_handles[game_id] = torrent_handle + torrent_handle.set_flags(lt.torrent_flags.auto_managed) + torrent_handle.resume() From 94b65c03579948d9e73f60f52420e0f452fa8244 Mon Sep 17 00:00:00 2001 From: Hachi-R Date: Fri, 8 Nov 2024 21:30:51 -0300 Subject: [PATCH 05/28] lint --- src/main/events/torrenting/pause-game-seed.ts | 5 ++--- src/main/events/torrenting/resume-game-seed.ts | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/events/torrenting/pause-game-seed.ts b/src/main/events/torrenting/pause-game-seed.ts index 3b00ddd50..d9a2a61a5 100644 --- a/src/main/events/torrenting/pause-game-seed.ts +++ b/src/main/events/torrenting/pause-game-seed.ts @@ -9,10 +9,9 @@ const pauseGameSeed = async ( gameId: number ) => { await dataSource.transaction(async (transactionalEntityManager) => { - await transactionalEntityManager - .getRepository(Game) - .update({ id: gameId }, { status: "complete", shouldSeed: false }); + .getRepository(Game) + .update({ id: gameId }, { status: "complete", shouldSeed: false }); }); await DownloadManager.pauseSeeding(gameId); diff --git a/src/main/events/torrenting/resume-game-seed.ts b/src/main/events/torrenting/resume-game-seed.ts index 133b26a03..6e74679d1 100644 --- a/src/main/events/torrenting/resume-game-seed.ts +++ b/src/main/events/torrenting/resume-game-seed.ts @@ -19,10 +19,9 @@ const resumeGameSeed = async ( if (!game) return; await dataSource.transaction(async (transactionalEntityManager) => { - await transactionalEntityManager - .getRepository(Game) - .update({ id: gameId }, { status: "seeding", shouldSeed: true }); + .getRepository(Game) + .update({ id: gameId }, { status: "seeding", shouldSeed: true }); }); await DownloadManager.resumeSeeding(gameId, game.uri!, game.downloadPath!); From 7c039ead1006ba086ae0cec2ac9861031dd63998 Mon Sep 17 00:00:00 2001 From: Hachi-R Date: Fri, 8 Nov 2024 23:36:43 -0300 Subject: [PATCH 06/28] feat: add seeding management logic --- .../services/download/download-manager.ts | 80 ++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts index 1f9383e17..2fb197062 100644 --- a/src/main/services/download/download-manager.ts +++ b/src/main/services/download/download-manager.ts @@ -2,12 +2,17 @@ import { Game } from "@main/entity"; import { Downloader } from "@shared"; import { PythonInstance } from "./python-instance"; import { WindowManager } from "../window-manager"; -import { downloadQueueRepository, gameRepository } from "@main/repository"; +import { + downloadQueueRepository, + gameRepository, + userPreferencesRepository, +} from "@main/repository"; import { publishDownloadCompleteNotification } from "../notifications"; import { RealDebridDownloader } from "./real-debrid-downloader"; import type { DownloadProgress } from "@types"; import { GofileApi, QiwiApi } from "../hosters"; import { GenericHttpDownloader } from "./generic-http-downloader"; +import { In, Not } from "typeorm"; export class DownloadManager { private static currentDownloader: Downloader | null = null; @@ -66,6 +71,79 @@ export class DownloadManager { } } + public static async getSeedStatus() { + const gamesToSeed = await gameRepository.find({ + where: { shouldSeed: true, isDeleted: false }, + }); + + if (gamesToSeed.length === 0) return; + + const seedStatus = await PythonInstance.getSeedStatus(); + + if (seedStatus.length === 0) { + for (const game of gamesToSeed) { + if (game.uri && game.downloadPath) { + await this.resumeSeeding(game.id, game.uri, game.downloadPath); + } + } + } + + const gameIds = seedStatus.map((status) => status.gameId); + const updateList = await gameRepository.find({ + where: { + id: In(gameIds), + status: Not(In(["complete", "seeding"])), + shouldSeed: true, + isDeleted: false, + }, + }); + + if (updateList.length > 0) { + await gameRepository.update( + { id: In(updateList.map((game) => game.id)) }, + { status: "seeding" } + ); + } + + const userPreferences = await userPreferencesRepository.findOneBy({ + id: 1, + }); + + const shouldSeedOrNot = await gameRepository.find({ + where: { + id: In(gameIds), + shouldSeed: false, + isDeleted: false, + status: Not(In(["complete", "seeding"])), + }, + }); + + if (shouldSeedOrNot.length === 0) return; + + if (userPreferences?.seedAfterDownloadComplete) { + await gameRepository.update( + { id: In(shouldSeedOrNot.map((game) => game.id)) }, + { shouldSeed: true, status: "seeding" } + ); + } else { + await gameRepository.update( + { id: In(shouldSeedOrNot.map((game) => game.id)) }, + { shouldSeed: false, status: "complete" } + ); + for (const game of shouldSeedOrNot) { + await this.pauseSeeding(game.id); + } + } + } + + static async pauseSeeding(gameId: number) { + await PythonInstance.pauseSeeding(gameId); + } + + static async resumeSeeding(gameId: number, magnet: string, savePath: string) { + await PythonInstance.resumeSeeding(gameId, magnet, savePath); + } + static async pauseDownload() { if (this.currentDownloader === Downloader.Torrent) { await PythonInstance.pauseDownload(); From 5078946191d6cb2ded873b7442257d2fc68096be Mon Sep 17 00:00:00 2001 From: Hachi-R Date: Sat, 9 Nov 2024 01:16:03 -0300 Subject: [PATCH 07/28] refactor: change logic to seed new downloads --- .../services/download/download-manager.ts | 31 ------------------- src/main/services/download/python-instance.ts | 18 ++++++++++- 2 files changed, 17 insertions(+), 32 deletions(-) diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts index 2fb197062..b14e81d79 100644 --- a/src/main/services/download/download-manager.ts +++ b/src/main/services/download/download-manager.ts @@ -5,7 +5,6 @@ import { WindowManager } from "../window-manager"; import { downloadQueueRepository, gameRepository, - userPreferencesRepository, } from "@main/repository"; import { publishDownloadCompleteNotification } from "../notifications"; import { RealDebridDownloader } from "./real-debrid-downloader"; @@ -104,36 +103,6 @@ export class DownloadManager { { status: "seeding" } ); } - - const userPreferences = await userPreferencesRepository.findOneBy({ - id: 1, - }); - - const shouldSeedOrNot = await gameRepository.find({ - where: { - id: In(gameIds), - shouldSeed: false, - isDeleted: false, - status: Not(In(["complete", "seeding"])), - }, - }); - - if (shouldSeedOrNot.length === 0) return; - - if (userPreferences?.seedAfterDownloadComplete) { - await gameRepository.update( - { id: In(shouldSeedOrNot.map((game) => game.id)) }, - { shouldSeed: true, status: "seeding" } - ); - } else { - await gameRepository.update( - { id: In(shouldSeedOrNot.map((game) => game.id)) }, - { shouldSeed: false, status: "complete" } - ); - for (const game of shouldSeedOrNot) { - await this.pauseSeeding(game.id); - } - } } static async pauseSeeding(gameId: number) { diff --git a/src/main/services/download/python-instance.ts b/src/main/services/download/python-instance.ts index 95997c4cd..737d54968 100644 --- a/src/main/services/download/python-instance.ts +++ b/src/main/services/download/python-instance.ts @@ -6,7 +6,7 @@ import { RPC_PORT, startTorrentClient as startRPCClient, } from "./torrent-client"; -import { gameRepository } from "@main/repository"; +import { gameRepository, userPreferencesRepository } from "@main/repository"; import type { DownloadProgress } from "@types"; import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity"; import { calculateETA } from "./helpers"; @@ -107,6 +107,22 @@ export class PythonInstance { } if (progress === 1 && !isCheckingFiles) { + const userPreferences = await userPreferencesRepository.findOneBy({ + id: 1, + }); + + if (userPreferences?.seedAfterDownloadComplete) { + gameRepository.update( + { id: gameId }, + { status: "seeding", shouldSeed: true } + ); + } else { + gameRepository.update( + { id: gameId }, + { status: "complete", shouldSeed: false } + ); + } + this.downloadingGameId = -1; } From c314c397a5102f18e32ee5867d2d76c8379f19b1 Mon Sep 17 00:00:00 2001 From: Hachi-R Date: Sat, 9 Nov 2024 01:29:06 -0300 Subject: [PATCH 08/28] feat: add option to disable seeding after download completes --- src/main/services/download/download-manager.ts | 5 +---- src/main/services/download/python-instance.ts | 2 ++ .../src/pages/settings/settings-behavior.tsx | 12 ++++++++++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts index b14e81d79..d56544cca 100644 --- a/src/main/services/download/download-manager.ts +++ b/src/main/services/download/download-manager.ts @@ -2,10 +2,7 @@ import { Game } from "@main/entity"; import { Downloader } from "@shared"; import { PythonInstance } from "./python-instance"; import { WindowManager } from "../window-manager"; -import { - downloadQueueRepository, - gameRepository, -} from "@main/repository"; +import { downloadQueueRepository, gameRepository } from "@main/repository"; import { publishDownloadCompleteNotification } from "../notifications"; import { RealDebridDownloader } from "./real-debrid-downloader"; import type { DownloadProgress } from "@types"; diff --git a/src/main/services/download/python-instance.ts b/src/main/services/download/python-instance.ts index 737d54968..69b4472d3 100644 --- a/src/main/services/download/python-instance.ts +++ b/src/main/services/download/python-instance.ts @@ -121,6 +121,8 @@ export class PythonInstance { { id: gameId }, { status: "complete", shouldSeed: false } ); + + this.pauseSeeding(gameId); } this.downloadingGameId = -1; diff --git a/src/renderer/src/pages/settings/settings-behavior.tsx b/src/renderer/src/pages/settings/settings-behavior.tsx index b4b91dd2b..c42f68f8f 100644 --- a/src/renderer/src/pages/settings/settings-behavior.tsx +++ b/src/renderer/src/pages/settings/settings-behavior.tsx @@ -19,6 +19,7 @@ export function SettingsBehavior() { runAtStartup: false, startMinimized: false, disableNsfwAlert: false, + seedAfterDownloadComplete: false, }); const { t } = useTranslation("settings"); @@ -30,6 +31,7 @@ export function SettingsBehavior() { runAtStartup: userPreferences.runAtStartup, startMinimized: userPreferences.startMinimized, disableNsfwAlert: userPreferences.disableNsfwAlert, + seedAfterDownloadComplete: userPreferences.seedAfterDownloadComplete, }); } }, [userPreferences]); @@ -96,6 +98,16 @@ export function SettingsBehavior() { handleChange({ disableNsfwAlert: !form.disableNsfwAlert }) } /> + + + handleChange({ + seedAfterDownloadComplete: !form.seedAfterDownloadComplete, + }) + } + /> ); } From 8ec52bf193c9be5d5d3360e010b39b9b048cad5e Mon Sep 17 00:00:00 2001 From: Hachi-R Date: Sat, 9 Nov 2024 12:08:58 -0300 Subject: [PATCH 09/28] temp --- .../services/download/download-manager.ts | 2 + .../src/pages/downloads/download-group.tsx | 46 +++++++++++++++++-- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts index d56544cca..241e48067 100644 --- a/src/main/services/download/download-manager.ts +++ b/src/main/services/download/download-manager.ts @@ -76,6 +76,8 @@ export class DownloadManager { const seedStatus = await PythonInstance.getSeedStatus(); + console.log(seedStatus); + if (seedStatus.length === 0) { for (const game of gamesToSeed) { if (game.uri && game.downloadPath) { diff --git a/src/renderer/src/pages/downloads/download-group.tsx b/src/renderer/src/pages/downloads/download-group.tsx index 7281079d6..34d44006b 100644 --- a/src/renderer/src/pages/downloads/download-group.tsx +++ b/src/renderer/src/pages/downloads/download-group.tsx @@ -15,6 +15,7 @@ import { useAppSelector, useDownload } from "@renderer/hooks"; import * as styles from "./download-group.css"; import { useTranslation } from "react-i18next"; import { SPACING_UNIT, vars } from "@renderer/theme.css"; +import { XCircleIcon } from "@primer/octicons-react"; export interface DownloadGroupProps { library: LibraryGame[]; @@ -44,6 +45,8 @@ export function DownloadGroup({ resumeDownload, cancelDownload, isGameDeleting, + pauseSeeding, + resumeSeeding, } = useDownload(); const getFinalDownloadSize = (game: LibraryGame) => { @@ -98,7 +101,13 @@ export function DownloadGroup({ } if (game.progress === 1) { - return

{t("completed")}

; + // return

{t("completed")}

; + + return game.status === "seeding" ? ( +

{t("seeding")}

+ ) : ( +

{t("completed")}

+ ); } if (game.status === "paused") { @@ -141,9 +150,15 @@ export function DownloadGroup({ {t("install")} - + {game.status === "seeding" ? ( + + ) : ( + + )} ); } @@ -207,7 +222,11 @@ export function DownloadGroup({