feat: pass seeding list from downloader.py to download page

This commit is contained in:
Hachi-R 2024-11-07 20:35:17 -03:00
parent 452532e18b
commit a9085ec2ed
12 changed files with 98 additions and 30 deletions

View File

@ -198,7 +198,10 @@
"queued": "Queued",
"no_downloads_title": "Such empty",
"no_downloads_description": "You haven't downloaded anything with Hydra yet, but it's never too late to start.",
"checking_files": "Checking files…"
"checking_files": "Checking files…",
"seeding": "Seeding",
"stop_seed": "Stop seed",
"resume_seed": "Resume seed"
},
"settings": {
"downloads_path": "Downloads path",

View File

@ -194,7 +194,10 @@
"queued": "Na fila",
"no_downloads_title": "Nada por aqui…",
"no_downloads_description": "Você ainda não baixou nada pelo Hydra, mas nunca é tarde para começar.",
"checking_files": "Verificando arquivos…"
"checking_files": "Verificando arquivos…",
"seeding": "Semeando",
"stop_seed": "Parar seed",
"resume_seed": "Retomar seed"
},
"settings": {
"downloads_path": "Diretório dos downloads",

View File

@ -54,6 +54,9 @@ export class Game {
@Column("int", { default: Downloader.Torrent })
downloader: Downloader;
@Column("boolean", { default: false })
shouldSeed: boolean;
/**
* Progress is a float between 0 and 1
*/

View File

@ -14,6 +14,7 @@ import { AddWinePrefixToGame } from "./migrations/20241019081648_add_wine_prefix
import { AddStartMinimizedColumn } from "./migrations/20241030171454_add_start_minimized_column";
import { AddSeedAfterDownloadCompletesColumn } from "./migrations/20241101012727_add_seed_after_download_completes_column";
import { AddSeedListTable } from "./migrations/20241103231555_add_seed_list_table";
import { AddShouldSeedColumn } from "./migrations/20241107211345_add_should_seed_colum";
export type HydraMigration = Knex.Migration & { name: string };
class MigrationSource implements Knex.MigrationSource<HydraMigration> {
@ -32,6 +33,7 @@ class MigrationSource implements Knex.MigrationSource<HydraMigration> {
AddStartMinimizedColumn,
AddSeedAfterDownloadCompletesColumn,
AddSeedListTable,
AddShouldSeedColumn,
]);
}
getMigrationName(migration: HydraMigration): string {

View File

@ -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");
});
},
};

View File

@ -5,7 +5,6 @@ import { WindowManager } from "../window-manager";
import {
downloadQueueRepository,
gameRepository,
seedListRepository,
userPreferencesRepository,
} from "@main/repository";
import { publishDownloadCompleteNotification } from "../notifications";
@ -63,20 +62,8 @@ export class DownloadManager {
userPreferences?.seedAfterDownloadCompletes &&
this.currentDownloader === Downloader.Torrent
) {
const existingSeed = await seedListRepository.findOne({
where: { downloadUri: game.uri! },
});
if (existingSeed) {
await seedListRepository.update(
{ downloadUri: game.uri! },
{ shouldSeed: true }
);
} else {
await seedListRepository.save({
downloadUri: game.uri!,
shouldSeed: true,
});
if (!game.shouldSeed) {
await gameRepository.update(game.id, { shouldSeed: true });
}
}
@ -97,9 +84,13 @@ export class DownloadManager {
}
public static async watchSeedingList() {
const seedingList = await PythonInstance.getSeedingList();
const shouldSeedGames = await gameRepository.findOne({
where: { shouldSeed: true },
});
if (shouldSeedGames) {
const seedingList = await PythonInstance.getSeedingList();
if (seedingList) {
WindowManager.mainWindow?.webContents.send(
"on-seeding-list",
JSON.parse(JSON.stringify(seedingList))

View File

@ -66,6 +66,16 @@ export class PythonInstance {
"/seed-list"
);
if (response.data && response.data.length > 0) {
for (const seed of response.data) {
await gameRepository.update(
{ id: seed.gameId },
{ status: "seeding" }
);
}
}
return response.data;
}

View File

@ -37,7 +37,6 @@ export interface LibtorrentSeedingPayload {
numPeers: number;
numSeeds: number;
uploadSpeed: number;
// isCheckingFiles: boolean;
fileSize: number;
folderName: string;
status: LibtorrentStatus;

View File

@ -1,6 +1,7 @@
import { useNavigate } from "react-router-dom";
import { useMemo } from "react";
import type { LibraryGame } from "@types";
import type { LibraryGame, SeedingList } from "@types";
import { Badge, Button } from "@renderer/components";
import {
@ -21,6 +22,7 @@ export interface DownloadGroupProps {
title: string;
openDeleteGameModal: (gameId: number) => void;
openGameInstaller: (gameId: number) => void;
seedingList: SeedingList[];
}
export function DownloadGroup({
@ -28,6 +30,7 @@ export function DownloadGroup({
title,
openDeleteGameModal,
openGameInstaller,
seedingList = [],
}: DownloadGroupProps) {
const navigate = useNavigate();
@ -46,6 +49,17 @@ export function DownloadGroup({
isGameDeleting,
} = useDownload();
const seedingMap = useMemo(() => {
if (!Array.isArray(seedingList) || seedingList.length === 0) {
return new Map<number, SeedingList>();
}
const map = new Map<number, SeedingList>();
seedingList.forEach((seed) => {
map.set(seed.gameId, seed);
});
return map;
}, [seedingList]);
const getFinalDownloadSize = (game: LibraryGame) => {
const isGameDownloading = lastPacket?.game.id === game.id;
@ -60,6 +74,7 @@ export function DownloadGroup({
const getGameInfo = (game: LibraryGame) => {
const isGameDownloading = lastPacket?.game.id === game.id;
const finalDownloadSize = getFinalDownloadSize(game);
const seed = seedingMap.get(game.id);
if (isGameDeleting(game.id)) {
return <p>{t("deleting")}</p>;
@ -98,7 +113,18 @@ export function DownloadGroup({
}
if (game.progress === 1) {
return <p>{t("completed")}</p>;
return (
<>
{seed ? (
<>
<p>{t("seeding")}</p>
<p>{formatBytes(seed.uploadSpeed ?? 0)}/s</p>
</>
) : (
<p>{t("completed")}</p>
)}
</>
);
}
if (game.status === "paused") {
@ -127,8 +153,8 @@ export function DownloadGroup({
const getGameActions = (game: LibraryGame) => {
const isGameDownloading = lastPacket?.game.id === game.id;
const deleting = isGameDeleting(game.id);
const seed = seedingMap.get(game.id);
if (game.progress === 1) {
return (
@ -144,6 +170,18 @@ export function DownloadGroup({
<Button onClick={() => openDeleteGameModal(game.id)} theme="outline">
{t("delete")}
</Button>
{seed && game.shouldSeed && (
<Button theme="outline">
{t("stop_seed")}
</Button>
)}
{seed && !game.shouldSeed && (
<Button theme="outline">
{t("resume_seed")}
</Button>
)}
</>
);
}

View File

@ -36,8 +36,6 @@ export default function Downloads() {
window.electron.onSeedingList((value) => setSeedingList(value));
}, []);
console.log("sexo", seedingList);
const handleOpenGameInstaller = (gameId: number) =>
window.electron.openGameInstaller(gameId).then((isBinaryInPath) => {
if (!isBinaryInPath) setShowBinaryNotFoundModal(true);
@ -130,6 +128,7 @@ export default function Downloads() {
library={group.library}
openDeleteGameModal={handleOpenDeleteGameModal}
openGameInstaller={handleOpenGameInstaller}
seedingList={seedingList}
/>
))}
</div>

View File

@ -7,7 +7,8 @@ export type GameStatus =
| "paused"
| "error"
| "complete"
| "removed";
| "removed"
| "seeding";
export type GameShop = "steam" | "epic";
@ -124,6 +125,7 @@ export interface Game {
objectID: string;
shop: GameShop;
downloadQueue: DownloadQueue | null;
shouldSeed: boolean;
createdAt: Date;
updatedAt: Date;
}
@ -156,6 +158,9 @@ export interface SeedingList {
numPeers: number;
numSeeds: number;
uploadSpeed: number;
gameId: number;
folderName: string;
fileSize: number;
}
export interface UserPreferences {

View File

@ -176,7 +176,7 @@ class TorrentDownloader:
torrent_info = {
'folderName': info.name() if info else "",
'fileSize': info.total_size() if info else 0,
'gameId': self.downloading_game_id,
'gameId': game_id,
'progress': status.progress,
'downloadSpeed': status.download_rate,
'uploadSpeed': status.upload_rate,
@ -189,6 +189,4 @@ class TorrentDownloader:
if status.state == 5:
response.append(torrent_info)
# print(response)
return response
# return None
return response