mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-03-09 15:40:26 +00:00
feat: adding aria2c
This commit is contained in:
parent
2d8b63c803
commit
4060f7a1a6
30 changed files with 371 additions and 1351 deletions
|
@ -1,42 +1,120 @@
|
|||
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";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import { PythonRPC } from "../python-rpc";
|
||||
import {
|
||||
LibtorrentPayload,
|
||||
LibtorrentStatus,
|
||||
PauseDownloadPayload,
|
||||
} from "./types";
|
||||
import { calculateETA } from "./helpers";
|
||||
import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity";
|
||||
import { RealDebridClient } from "./real-debrid";
|
||||
|
||||
export class DownloadManager {
|
||||
private static currentDownloader: Downloader | null = null;
|
||||
private static downloadingGameId: number | null = null;
|
||||
|
||||
public static async watchDownloads() {
|
||||
let status: DownloadProgress | null = null;
|
||||
private static async getDownloadStatus() {
|
||||
const response = await PythonRPC.rpc.get<LibtorrentPayload | null>(
|
||||
"/status"
|
||||
);
|
||||
|
||||
if (this.currentDownloader === Downloader.Torrent) {
|
||||
status = await PythonInstance.getStatus();
|
||||
} else if (this.currentDownloader === Downloader.RealDebrid) {
|
||||
status = await RealDebridDownloader.getStatus();
|
||||
} else {
|
||||
status = await GenericHttpDownloader.getStatus();
|
||||
if (response.data === null || !this.downloadingGameId) return null;
|
||||
|
||||
const gameId = this.downloadingGameId;
|
||||
|
||||
try {
|
||||
const {
|
||||
progress,
|
||||
numPeers,
|
||||
numSeeds,
|
||||
downloadSpeed,
|
||||
bytesDownloaded,
|
||||
fileSize,
|
||||
folderName,
|
||||
status,
|
||||
} = response.data;
|
||||
|
||||
const isDownloadingMetadata =
|
||||
status === LibtorrentStatus.DownloadingMetadata;
|
||||
|
||||
const isCheckingFiles = status === LibtorrentStatus.CheckingFiles;
|
||||
|
||||
if (!isDownloadingMetadata && !isCheckingFiles) {
|
||||
const update: QueryDeepPartialEntity<Game> = {
|
||||
bytesDownloaded,
|
||||
fileSize,
|
||||
progress,
|
||||
status: "active",
|
||||
};
|
||||
|
||||
await gameRepository.update(
|
||||
{ id: gameId },
|
||||
{
|
||||
...update,
|
||||
folderName,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
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.pauseSeeding(gameId);
|
||||
}
|
||||
|
||||
this.downloadingGameId = -1;
|
||||
}
|
||||
|
||||
return {
|
||||
numPeers,
|
||||
numSeeds,
|
||||
downloadSpeed,
|
||||
timeRemaining: calculateETA(fileSize, bytesDownloaded, downloadSpeed),
|
||||
isDownloadingMetadata,
|
||||
isCheckingFiles,
|
||||
progress,
|
||||
gameId,
|
||||
} as DownloadProgress;
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static async watchDownloads() {
|
||||
const status = await this.getDownloadStatus();
|
||||
|
||||
// // status = await RealDebridDownloader.getStatus();
|
||||
|
||||
if (status) {
|
||||
const { gameId, progress } = status;
|
||||
|
||||
const game = await gameRepository.findOne({
|
||||
where: { id: gameId, isDeleted: false },
|
||||
});
|
||||
|
||||
if (WindowManager.mainWindow && game) {
|
||||
WindowManager.mainWindow.setProgressBar(progress === 1 ? -1 : progress);
|
||||
|
||||
WindowManager.mainWindow.webContents.send(
|
||||
"on-download-progress",
|
||||
JSON.parse(
|
||||
|
@ -47,12 +125,9 @@ export class DownloadManager {
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (progress === 1 && game) {
|
||||
publishDownloadCompleteNotification(game);
|
||||
|
||||
await downloadQueueRepository.delete({ game });
|
||||
|
||||
const [nextQueueItem] = await downloadQueueRepository.find({
|
||||
order: {
|
||||
id: "DESC",
|
||||
|
@ -61,7 +136,6 @@ export class DownloadManager {
|
|||
game: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (nextQueueItem) {
|
||||
this.resumeDownload(nextQueueItem.game);
|
||||
}
|
||||
|
@ -70,88 +144,80 @@ 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);
|
||||
|
||||
for (const gameId of gameIds) {
|
||||
const game = await gameRepository.findOne({
|
||||
where: { id: gameId },
|
||||
});
|
||||
|
||||
if (game) {
|
||||
const isNotDeleted = fs.existsSync(
|
||||
path.join(game.downloadPath!, game.folderName!)
|
||||
);
|
||||
|
||||
if (!isNotDeleted) {
|
||||
await this.pauseSeeding(game.id);
|
||||
|
||||
await gameRepository.update(game.id, {
|
||||
status: "complete",
|
||||
shouldSeed: false,
|
||||
});
|
||||
|
||||
WindowManager.mainWindow?.webContents.send("on-hard-delete");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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" }
|
||||
);
|
||||
}
|
||||
|
||||
WindowManager.mainWindow?.webContents.send(
|
||||
"on-seeding-status",
|
||||
JSON.parse(JSON.stringify(seedStatus))
|
||||
);
|
||||
// const gamesToSeed = await gameRepository.find({
|
||||
// where: { shouldSeed: true, isDeleted: false },
|
||||
// });
|
||||
// if (gamesToSeed.length === 0) return;
|
||||
// const seedStatus = await PythonRPC.rpc
|
||||
// .get<LibtorrentPayload[] | null>("/seed-status")
|
||||
// .then((results) => {
|
||||
// if (results === null) return [];
|
||||
// return results.data;
|
||||
// });
|
||||
// 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);
|
||||
// for (const gameId of gameIds) {
|
||||
// const game = await gameRepository.findOne({
|
||||
// where: { id: gameId },
|
||||
// });
|
||||
// if (game) {
|
||||
// const isNotDeleted = fs.existsSync(
|
||||
// path.join(game.downloadPath!, game.folderName!)
|
||||
// );
|
||||
// if (!isNotDeleted) {
|
||||
// await this.pauseSeeding(game.id);
|
||||
// await gameRepository.update(game.id, {
|
||||
// status: "complete",
|
||||
// shouldSeed: false,
|
||||
// });
|
||||
// WindowManager.mainWindow?.webContents.send("on-hard-delete");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// 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" }
|
||||
// );
|
||||
// }
|
||||
// WindowManager.mainWindow?.webContents.send(
|
||||
// "on-seeding-status",
|
||||
// JSON.parse(JSON.stringify(seedStatus))
|
||||
// );
|
||||
}
|
||||
|
||||
static async pauseSeeding(gameId: number) {
|
||||
await PythonInstance.pauseSeeding(gameId);
|
||||
// await TorrentDownloader.pauseSeeding(gameId);
|
||||
}
|
||||
|
||||
static async resumeSeeding(gameId: number, magnet: string, savePath: string) {
|
||||
await PythonInstance.resumeSeeding(gameId, magnet, savePath);
|
||||
// await TorrentDownloader.resumeSeeding(gameId, magnet, savePath);
|
||||
}
|
||||
|
||||
static async pauseDownload() {
|
||||
if (this.currentDownloader === Downloader.Torrent) {
|
||||
await PythonInstance.pauseDownload();
|
||||
} else if (this.currentDownloader === Downloader.RealDebrid) {
|
||||
await RealDebridDownloader.pauseDownload();
|
||||
} else {
|
||||
await GenericHttpDownloader.pauseDownload();
|
||||
}
|
||||
await PythonRPC.rpc
|
||||
.post("/action", {
|
||||
action: "pause",
|
||||
game_id: this.downloadingGameId,
|
||||
} as PauseDownloadPayload)
|
||||
.catch(() => {});
|
||||
|
||||
WindowManager.mainWindow?.setProgressBar(-1);
|
||||
this.currentDownloader = null;
|
||||
|
||||
this.downloadingGameId = null;
|
||||
}
|
||||
|
||||
|
@ -160,16 +226,13 @@ export class DownloadManager {
|
|||
}
|
||||
|
||||
static async cancelDownload(gameId = this.downloadingGameId!) {
|
||||
if (this.currentDownloader === Downloader.Torrent) {
|
||||
PythonInstance.cancelDownload(gameId);
|
||||
} else if (this.currentDownloader === Downloader.RealDebrid) {
|
||||
RealDebridDownloader.cancelDownload(gameId);
|
||||
} else {
|
||||
GenericHttpDownloader.cancelDownload(gameId);
|
||||
}
|
||||
await PythonRPC.rpc.post("/action", {
|
||||
action: "cancel",
|
||||
game_id: gameId,
|
||||
});
|
||||
|
||||
WindowManager.mainWindow?.setProgressBar(-1);
|
||||
this.currentDownloader = null;
|
||||
|
||||
this.downloadingGameId = null;
|
||||
}
|
||||
|
||||
|
@ -181,34 +244,57 @@ export class DownloadManager {
|
|||
const token = await GofileApi.authorize();
|
||||
const downloadLink = await GofileApi.getDownloadLink(id!);
|
||||
|
||||
GenericHttpDownloader.startDownload(game, downloadLink, {
|
||||
Cookie: `accountToken=${token}`,
|
||||
await PythonRPC.rpc.post("/action", {
|
||||
action: "start",
|
||||
game_id: game.id,
|
||||
url: downloadLink,
|
||||
save_path: game.downloadPath,
|
||||
header: `Cookie: accountToken=${token}`,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case Downloader.PixelDrain: {
|
||||
const id = game!.uri!.split("/").pop();
|
||||
|
||||
await GenericHttpDownloader.startDownload(
|
||||
game,
|
||||
`https://pixeldrain.com/api/file/${id}?download`
|
||||
);
|
||||
await PythonRPC.rpc.post("/action", {
|
||||
action: "start",
|
||||
game_id: game.id,
|
||||
url: `https://pixeldrain.com/api/file/${id}?download`,
|
||||
save_path: game.downloadPath,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case Downloader.Qiwi: {
|
||||
const downloadUrl = await QiwiApi.getDownloadUrl(game.uri!);
|
||||
|
||||
await GenericHttpDownloader.startDownload(game, downloadUrl);
|
||||
await PythonRPC.rpc.post("/action", {
|
||||
action: "start",
|
||||
game_id: game.id,
|
||||
url: downloadUrl,
|
||||
save_path: game.downloadPath,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case Downloader.Torrent:
|
||||
PythonInstance.startDownload(game);
|
||||
await PythonRPC.rpc.post("/action", {
|
||||
action: "start",
|
||||
game_id: game.id,
|
||||
url: game.uri,
|
||||
save_path: game.downloadPath,
|
||||
});
|
||||
break;
|
||||
case Downloader.RealDebrid:
|
||||
RealDebridDownloader.startDownload(game);
|
||||
case Downloader.RealDebrid: {
|
||||
const downloadUrl = await RealDebridClient.getDownloadUrl(game.uri!);
|
||||
|
||||
await PythonRPC.rpc.post("/action", {
|
||||
action: "start",
|
||||
game_id: game.id,
|
||||
url: downloadUrl,
|
||||
save_path: game.downloadPath,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.currentDownloader = game.downloader;
|
||||
this.downloadingGameId = game.id;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue