mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-03-09 15:40:26 +00:00
Merge pull request #211 from lilezek/feat/real-debrid-integration
Improvements over the real debrid integration.
This commit is contained in:
commit
13be9c3814
8 changed files with 688 additions and 463 deletions
|
@ -51,7 +51,7 @@
|
|||
"jsdom": "^24.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lottie-react": "^2.4.0",
|
||||
"node-unrar-js": "^2.0.2",
|
||||
"node-7z-archive": "^1.1.7",
|
||||
"parse-torrent": "^11.0.16",
|
||||
"ps-list": "^8.1.1",
|
||||
"react-i18next": "^14.1.0",
|
||||
|
|
|
@ -26,7 +26,7 @@ const startGameDownload = async (
|
|||
});
|
||||
|
||||
const downloader = userPreferences?.realDebridApiToken
|
||||
? Downloader.Http
|
||||
? Downloader.RealDebrid
|
||||
: Downloader.Torrent;
|
||||
|
||||
const [game, repack] = await Promise.all([
|
||||
|
|
|
@ -4,7 +4,7 @@ import type { Game } from "@main/entity";
|
|||
import { Downloader } from "@shared";
|
||||
|
||||
import { writePipe } from "./fifo";
|
||||
import { HTTPDownloader } from "./downloaders";
|
||||
import { RealDebridDownloader } from "./downloaders";
|
||||
|
||||
export class DownloadManager {
|
||||
private static gameDownloading: Game;
|
||||
|
@ -25,7 +25,7 @@ export class DownloadManager {
|
|||
) {
|
||||
writePipe.write({ action: "cancel" });
|
||||
} else {
|
||||
HTTPDownloader.destroy();
|
||||
RealDebridDownloader.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ export class DownloadManager {
|
|||
) {
|
||||
writePipe.write({ action: "pause" });
|
||||
} else {
|
||||
HTTPDownloader.destroy();
|
||||
RealDebridDownloader.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ export class DownloadManager {
|
|||
save_path: game!.downloadPath,
|
||||
});
|
||||
} else {
|
||||
HTTPDownloader.startDownload(game!);
|
||||
RealDebridDownloader.startDownload(game!);
|
||||
}
|
||||
|
||||
this.gameDownloading = game!;
|
||||
|
@ -68,7 +68,7 @@ export class DownloadManager {
|
|||
save_path: game!.downloadPath,
|
||||
});
|
||||
} else {
|
||||
HTTPDownloader.startDownload(game!);
|
||||
RealDebridDownloader.startDownload(game!);
|
||||
}
|
||||
|
||||
this.gameDownloading = game!;
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
export * from "./http.downloader";
|
||||
export * from "./real-debrid.downloader";
|
||||
export * from "./torrent.downloader";
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
import { Game } from "@main/entity";
|
||||
import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity";
|
||||
import path from "node:path";
|
||||
import fs from "node:fs";
|
||||
import EasyDL from "easydl";
|
||||
import { GameStatus } from "@shared";
|
||||
import { fullArchive } from "node-7z-archive";
|
||||
|
||||
import { Downloader } from "./downloader";
|
||||
import { RealDebridClient } from "../real-debrid";
|
||||
|
||||
export class HTTPDownloader extends Downloader {
|
||||
function getFileNameWithoutExtension(fullPath: string) {
|
||||
return path.basename(fullPath, path.extname(fullPath));
|
||||
}
|
||||
|
||||
export class RealDebridDownloader extends Downloader {
|
||||
private static download: EasyDL;
|
||||
private static downloadSize = 0;
|
||||
|
||||
|
@ -22,52 +26,14 @@ export class HTTPDownloader extends Downloader {
|
|||
return 1;
|
||||
}
|
||||
|
||||
static async getDownloadUrl(game: Game) {
|
||||
const torrents = await RealDebridClient.getAllTorrentsFromUser();
|
||||
const hash = RealDebridClient.extractSHA1FromMagnet(game!.repack.magnet);
|
||||
let torrent = torrents.find((t) => t.hash === hash);
|
||||
|
||||
if (!torrent) {
|
||||
const magnet = await RealDebridClient.addMagnet(game!.repack.magnet);
|
||||
|
||||
if (magnet && magnet.id) {
|
||||
await RealDebridClient.selectAllFiles(magnet.id);
|
||||
torrent = await RealDebridClient.getInfo(magnet.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (torrent) {
|
||||
const { links } = torrent;
|
||||
const { download } = await RealDebridClient.unrestrictLink(links[0]);
|
||||
|
||||
if (!download) {
|
||||
throw new Error("Torrent not cached on Real Debrid");
|
||||
}
|
||||
|
||||
return download;
|
||||
}
|
||||
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
private static createFolderIfNotExists(path: string) {
|
||||
if (!fs.existsSync(path)) {
|
||||
fs.mkdirSync(path);
|
||||
}
|
||||
}
|
||||
|
||||
static async startDownload(game: Game) {
|
||||
if (this.download) this.download.destroy();
|
||||
const downloadUrl = await this.getDownloadUrl(game);
|
||||
const downloadUrl = await RealDebridClient.getDownloadUrl(game);
|
||||
|
||||
const filename = path.basename(downloadUrl);
|
||||
const folderName = path.basename(filename, path.extname(filename));
|
||||
|
||||
const fullDownloadPath = path.join(game.downloadPath!, folderName);
|
||||
|
||||
this.createFolderIfNotExists(fullDownloadPath);
|
||||
|
||||
this.download = new EasyDL(downloadUrl, fullDownloadPath);
|
||||
this.download = new EasyDL(
|
||||
downloadUrl,
|
||||
path.join(game.downloadPath!, ".rd")
|
||||
);
|
||||
|
||||
const metadata = await this.download.metadata();
|
||||
|
||||
|
@ -76,7 +42,7 @@ export class HTTPDownloader extends Downloader {
|
|||
const updatePayload: QueryDeepPartialEntity<Game> = {
|
||||
status: GameStatus.Downloading,
|
||||
fileSize: metadata.size,
|
||||
folderName: folderName,
|
||||
folderName: getFileNameWithoutExtension(metadata.savedFilePath),
|
||||
};
|
||||
|
||||
const downloadStatus = {
|
||||
|
@ -87,11 +53,8 @@ export class HTTPDownloader extends Downloader {
|
|||
|
||||
this.download.on("progress", async ({ total }) => {
|
||||
const updatePayload: QueryDeepPartialEntity<Game> = {
|
||||
status:
|
||||
total.percentage === 100
|
||||
? GameStatus.Finished
|
||||
: GameStatus.Downloading,
|
||||
progress: total.percentage / 100,
|
||||
status: GameStatus.Downloading,
|
||||
progress: Math.min(0.99, total.percentage / 100),
|
||||
bytesDownloaded: total.bytes,
|
||||
};
|
||||
|
||||
|
@ -102,6 +65,33 @@ export class HTTPDownloader extends Downloader {
|
|||
|
||||
await this.updateGameProgress(game.id, updatePayload, downloadStatus);
|
||||
});
|
||||
|
||||
this.download.on("end", async () => {
|
||||
const updatePayload: QueryDeepPartialEntity<Game> = {
|
||||
status: GameStatus.Decompressing,
|
||||
progress: 0.99,
|
||||
};
|
||||
|
||||
await this.updateGameProgress(game.id, updatePayload, {
|
||||
timeRemaining: 0,
|
||||
});
|
||||
|
||||
this.startDecompression(this.download.savedFilePath!, getFileNameWithoutExtension(metadata.savedFilePath), game);
|
||||
});
|
||||
}
|
||||
|
||||
static async startDecompression(rarFile: string, destiny: string, game: Game) {
|
||||
const directory = path.join(game.downloadPath!, destiny);
|
||||
|
||||
await fullArchive(rarFile, directory);
|
||||
const updatePayload: QueryDeepPartialEntity<Game> = {
|
||||
status: GameStatus.Finished,
|
||||
progress: 1,
|
||||
};
|
||||
|
||||
await this.updateGameProgress(game.id, updatePayload, {
|
||||
timeRemaining: 0,
|
||||
});
|
||||
}
|
||||
|
||||
static destroy() {
|
|
@ -62,6 +62,34 @@ export class RealDebridClient {
|
|||
return magnet.match(/btih:([0-9a-fA-F]*)/)?.[1].toLowerCase();
|
||||
}
|
||||
|
||||
static async getDownloadUrl(game: Game) {
|
||||
const torrents = await RealDebridClient.getAllTorrentsFromUser();
|
||||
const hash = RealDebridClient.extractSHA1FromMagnet(game!.repack.magnet);
|
||||
let torrent = torrents.find((t) => t.hash === hash);
|
||||
|
||||
if (!torrent) {
|
||||
const magnet = await RealDebridClient.addMagnet(game!.repack.magnet);
|
||||
|
||||
if (magnet && magnet.id) {
|
||||
await RealDebridClient.selectAllFiles(magnet.id);
|
||||
torrent = await RealDebridClient.getInfo(magnet.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (torrent) {
|
||||
const { links } = torrent;
|
||||
const { download } = await RealDebridClient.unrestrictLink(links[0]);
|
||||
|
||||
if (!download) {
|
||||
throw new Error("Torrent not cached on Real Debrid");
|
||||
}
|
||||
|
||||
return download;
|
||||
}
|
||||
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
static async authorize(apiToken: string) {
|
||||
this.instance = axios.create({
|
||||
baseURL: base,
|
||||
|
|
|
@ -5,11 +5,12 @@ export enum GameStatus {
|
|||
CheckingFiles = "checking_files",
|
||||
DownloadingMetadata = "downloading_metadata",
|
||||
Cancelled = "cancelled",
|
||||
Decompressing = "decompressing",
|
||||
Finished = "finished",
|
||||
}
|
||||
|
||||
export enum Downloader {
|
||||
Http,
|
||||
RealDebrid,
|
||||
Torrent,
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue