mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-27 12:51:03 +00:00
Merge branch 'main' into hyd-332-friendsblocks-list-pagination-and-set-privacy-settings
# Conflicts: # src/renderer/src/pages/user/user-content.tsx
This commit is contained in:
80
src/main/declaration.d.ts
vendored
80
src/main/declaration.d.ts
vendored
@@ -1,80 +0,0 @@
|
||||
declare module "aria2" {
|
||||
export type Aria2Status =
|
||||
| "active"
|
||||
| "waiting"
|
||||
| "paused"
|
||||
| "error"
|
||||
| "complete"
|
||||
| "removed";
|
||||
|
||||
export interface StatusResponse {
|
||||
gid: string;
|
||||
status: Aria2Status;
|
||||
totalLength: string;
|
||||
completedLength: string;
|
||||
uploadLength: string;
|
||||
bitfield: string;
|
||||
downloadSpeed: string;
|
||||
uploadSpeed: string;
|
||||
infoHash?: string;
|
||||
numSeeders?: string;
|
||||
seeder?: boolean;
|
||||
pieceLength: string;
|
||||
numPieces: string;
|
||||
connections: string;
|
||||
errorCode?: string;
|
||||
errorMessage?: string;
|
||||
followedBy?: string[];
|
||||
following: string;
|
||||
belongsTo: string;
|
||||
dir: string;
|
||||
files: {
|
||||
path: string;
|
||||
length: string;
|
||||
completedLength: string;
|
||||
selected: string;
|
||||
}[];
|
||||
bittorrent?: {
|
||||
announceList: string[][];
|
||||
comment: string;
|
||||
creationDate: string;
|
||||
mode: "single" | "multi";
|
||||
info: {
|
||||
name: string;
|
||||
verifiedLength: string;
|
||||
verifyIntegrityPending: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export default class Aria2 {
|
||||
constructor(options: any);
|
||||
open: () => Promise<void>;
|
||||
call(
|
||||
method: "addUri",
|
||||
uris: string[],
|
||||
options: { dir: string }
|
||||
): Promise<string>;
|
||||
call(
|
||||
method: "tellStatus",
|
||||
gid: string,
|
||||
keys?: string[]
|
||||
): Promise<StatusResponse>;
|
||||
call(method: "pause", gid: string): Promise<string>;
|
||||
call(method: "forcePause", gid: string): Promise<string>;
|
||||
call(method: "unpause", gid: string): Promise<string>;
|
||||
call(method: "remove", gid: string): Promise<string>;
|
||||
call(method: "forceRemove", gid: string): Promise<string>;
|
||||
call(method: "pauseAll"): Promise<string>;
|
||||
call(method: "forcePauseAll"): Promise<string>;
|
||||
listNotifications: () => [
|
||||
"onDownloadStart",
|
||||
"onDownloadPause",
|
||||
"onDownloadStop",
|
||||
"onDownloadComplete",
|
||||
"onDownloadError",
|
||||
"onBtDownloadComplete",
|
||||
];
|
||||
on: (event: string, callback: (params: any) => void) => void;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,8 @@ const signOut = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||
/* Disconnects libtorrent */
|
||||
PythonInstance.killTorrent();
|
||||
|
||||
HydraApi.handleSignOut();
|
||||
|
||||
await Promise.all([
|
||||
databaseOperations,
|
||||
HydraApi.post("/auth/logout").catch(() => {}),
|
||||
|
||||
@@ -22,8 +22,9 @@ const loadState = async (userPreferences: UserPreferences | null) => {
|
||||
|
||||
import("./events");
|
||||
|
||||
if (userPreferences?.realDebridApiToken)
|
||||
if (userPreferences?.realDebridApiToken) {
|
||||
RealDebridClient.authorize(userPreferences?.realDebridApiToken);
|
||||
}
|
||||
|
||||
HydraApi.setupApi().then(() => {
|
||||
uploadGamesBatch();
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import path from "node:path";
|
||||
import { spawn } from "node:child_process";
|
||||
import { app } from "electron";
|
||||
|
||||
export const startAria2 = () => {
|
||||
const binaryPath = app.isPackaged
|
||||
? path.join(process.resourcesPath, "aria2", "aria2c")
|
||||
: path.join(__dirname, "..", "..", "aria2", "aria2c");
|
||||
|
||||
return spawn(
|
||||
binaryPath,
|
||||
[
|
||||
"--enable-rpc",
|
||||
"--rpc-listen-all",
|
||||
"--file-allocation=none",
|
||||
"--allow-overwrite=true",
|
||||
],
|
||||
{ stdio: "inherit", windowsHide: true }
|
||||
);
|
||||
};
|
||||
@@ -6,6 +6,8 @@ import { downloadQueueRepository, gameRepository } from "@main/repository";
|
||||
import { publishDownloadCompleteNotification } from "../notifications";
|
||||
import { RealDebridDownloader } from "./real-debrid-downloader";
|
||||
import type { DownloadProgress } from "@types";
|
||||
import { GofileApi } from "../hosters";
|
||||
import { GenericHTTPDownloader } from "./generic-http-downloader";
|
||||
|
||||
export class DownloadManager {
|
||||
private static currentDownloader: Downloader | null = null;
|
||||
@@ -13,10 +15,12 @@ export class DownloadManager {
|
||||
public static async watchDownloads() {
|
||||
let status: DownloadProgress | null = null;
|
||||
|
||||
if (this.currentDownloader === Downloader.RealDebrid) {
|
||||
if (this.currentDownloader === Downloader.Torrent) {
|
||||
status = await PythonInstance.getStatus();
|
||||
} else if (this.currentDownloader === Downloader.RealDebrid) {
|
||||
status = await RealDebridDownloader.getStatus();
|
||||
} else {
|
||||
status = await PythonInstance.getStatus();
|
||||
status = await GenericHTTPDownloader.getStatus();
|
||||
}
|
||||
|
||||
if (status) {
|
||||
@@ -62,10 +66,12 @@ export class DownloadManager {
|
||||
}
|
||||
|
||||
static async pauseDownload() {
|
||||
if (this.currentDownloader === Downloader.RealDebrid) {
|
||||
if (this.currentDownloader === Downloader.Torrent) {
|
||||
await PythonInstance.pauseDownload();
|
||||
} else if (this.currentDownloader === Downloader.RealDebrid) {
|
||||
await RealDebridDownloader.pauseDownload();
|
||||
} else {
|
||||
await PythonInstance.pauseDownload();
|
||||
await GenericHTTPDownloader.pauseDownload();
|
||||
}
|
||||
|
||||
WindowManager.mainWindow?.setProgressBar(-1);
|
||||
@@ -73,20 +79,16 @@ export class DownloadManager {
|
||||
}
|
||||
|
||||
static async resumeDownload(game: Game) {
|
||||
if (game.downloader === Downloader.RealDebrid) {
|
||||
RealDebridDownloader.startDownload(game);
|
||||
this.currentDownloader = Downloader.RealDebrid;
|
||||
} else {
|
||||
PythonInstance.startDownload(game);
|
||||
this.currentDownloader = Downloader.Torrent;
|
||||
}
|
||||
return this.startDownload(game);
|
||||
}
|
||||
|
||||
static async cancelDownload(gameId: number) {
|
||||
if (this.currentDownloader === Downloader.RealDebrid) {
|
||||
if (this.currentDownloader === Downloader.Torrent) {
|
||||
PythonInstance.cancelDownload(gameId);
|
||||
} else if (this.currentDownloader === Downloader.RealDebrid) {
|
||||
RealDebridDownloader.cancelDownload(gameId);
|
||||
} else {
|
||||
PythonInstance.cancelDownload(gameId);
|
||||
GenericHTTPDownloader.cancelDownload(gameId);
|
||||
}
|
||||
|
||||
WindowManager.mainWindow?.setProgressBar(-1);
|
||||
@@ -94,12 +96,28 @@ export class DownloadManager {
|
||||
}
|
||||
|
||||
static async startDownload(game: Game) {
|
||||
if (game.downloader === Downloader.RealDebrid) {
|
||||
RealDebridDownloader.startDownload(game);
|
||||
this.currentDownloader = Downloader.RealDebrid;
|
||||
} else {
|
||||
if (game.downloader === Downloader.Gofile) {
|
||||
const id = game!.uri!.split("/").pop();
|
||||
|
||||
const token = await GofileApi.authorize();
|
||||
const downloadLink = await GofileApi.getDownloadLink(id!);
|
||||
|
||||
GenericHTTPDownloader.startDownload(game, downloadLink, {
|
||||
Cookie: `accountToken=${token}`,
|
||||
});
|
||||
} else if (game.downloader === Downloader.PixelDrain) {
|
||||
const id = game!.uri!.split("/").pop();
|
||||
|
||||
await GenericHTTPDownloader.startDownload(
|
||||
game,
|
||||
`https://pixeldrain.com/api/file/${id}?download`
|
||||
);
|
||||
} else if (game.downloader === Downloader.Torrent) {
|
||||
PythonInstance.startDownload(game);
|
||||
this.currentDownloader = Downloader.Torrent;
|
||||
} else if (game.downloader === Downloader.RealDebrid) {
|
||||
RealDebridDownloader.startDownload(game);
|
||||
}
|
||||
|
||||
this.currentDownloader = game.downloader;
|
||||
}
|
||||
}
|
||||
|
||||
107
src/main/services/download/generic-http-downloader.ts
Normal file
107
src/main/services/download/generic-http-downloader.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import { Game } from "@main/entity";
|
||||
import { gameRepository } from "@main/repository";
|
||||
import { calculateETA } from "./helpers";
|
||||
import { DownloadProgress } from "@types";
|
||||
import { HttpDownload } from "./http-download";
|
||||
|
||||
export class GenericHTTPDownloader {
|
||||
private static downloads = new Map<number, string>();
|
||||
private static downloadingGame: Game | null = null;
|
||||
|
||||
public static async getStatus() {
|
||||
if (this.downloadingGame) {
|
||||
const gid = this.downloads.get(this.downloadingGame.id)!;
|
||||
const status = HttpDownload.getStatus(gid);
|
||||
|
||||
if (status) {
|
||||
const progress =
|
||||
Number(status.completedLength) / Number(status.totalLength);
|
||||
|
||||
await gameRepository.update(
|
||||
{ id: this.downloadingGame!.id },
|
||||
{
|
||||
bytesDownloaded: Number(status.completedLength),
|
||||
fileSize: Number(status.totalLength),
|
||||
progress,
|
||||
status: "active",
|
||||
folderName: status.folderName,
|
||||
}
|
||||
);
|
||||
|
||||
const result = {
|
||||
numPeers: 0,
|
||||
numSeeds: 0,
|
||||
downloadSpeed: status.downloadSpeed,
|
||||
timeRemaining: calculateETA(
|
||||
status.totalLength,
|
||||
status.completedLength,
|
||||
status.downloadSpeed
|
||||
),
|
||||
isDownloadingMetadata: false,
|
||||
isCheckingFiles: false,
|
||||
progress,
|
||||
gameId: this.downloadingGame!.id,
|
||||
} as DownloadProgress;
|
||||
|
||||
if (progress === 1) {
|
||||
this.downloads.delete(this.downloadingGame.id);
|
||||
this.downloadingGame = null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static async pauseDownload() {
|
||||
if (this.downloadingGame) {
|
||||
const gid = this.downloads.get(this.downloadingGame!.id!);
|
||||
|
||||
if (gid) {
|
||||
await HttpDownload.pauseDownload(gid);
|
||||
}
|
||||
|
||||
this.downloadingGame = null;
|
||||
}
|
||||
}
|
||||
|
||||
static async startDownload(
|
||||
game: Game,
|
||||
downloadUrl: string,
|
||||
headers?: Record<string, string>
|
||||
) {
|
||||
this.downloadingGame = game;
|
||||
|
||||
if (this.downloads.has(game.id)) {
|
||||
await this.resumeDownload(game.id!);
|
||||
return;
|
||||
}
|
||||
|
||||
const gid = await HttpDownload.startDownload(
|
||||
game.downloadPath!,
|
||||
downloadUrl,
|
||||
headers
|
||||
);
|
||||
|
||||
this.downloads.set(game.id!, gid);
|
||||
}
|
||||
|
||||
static async cancelDownload(gameId: number) {
|
||||
const gid = this.downloads.get(gameId);
|
||||
|
||||
if (gid) {
|
||||
await HttpDownload.cancelDownload(gid);
|
||||
this.downloads.delete(gameId);
|
||||
}
|
||||
}
|
||||
|
||||
static async resumeDownload(gameId: number) {
|
||||
const gid = this.downloads.get(gameId);
|
||||
|
||||
if (gid) {
|
||||
await HttpDownload.resumeDownload(gid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,68 +1,69 @@
|
||||
import type { ChildProcess } from "node:child_process";
|
||||
import { logger } from "../logger";
|
||||
import { sleep } from "@main/helpers";
|
||||
import { startAria2 } from "../aria2c";
|
||||
import Aria2 from "aria2";
|
||||
import { DownloadItem } from "electron";
|
||||
import { WindowManager } from "../window-manager";
|
||||
import path from "node:path";
|
||||
|
||||
export class HttpDownload {
|
||||
private static connected = false;
|
||||
private static aria2c: ChildProcess | null = null;
|
||||
private static id = 0;
|
||||
|
||||
private static aria2 = new Aria2({});
|
||||
private static downloads: Record<string, DownloadItem> = {};
|
||||
|
||||
private static async connect() {
|
||||
this.aria2c = startAria2();
|
||||
|
||||
let retries = 0;
|
||||
|
||||
while (retries < 4 && !this.connected) {
|
||||
try {
|
||||
await this.aria2.open();
|
||||
logger.log("Connected to aria2");
|
||||
|
||||
this.connected = true;
|
||||
} catch (err) {
|
||||
await sleep(100);
|
||||
logger.log("Failed to connect to aria2, retrying...");
|
||||
retries++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static getStatus(gid: string) {
|
||||
if (this.connected) {
|
||||
return this.aria2.call("tellStatus", gid);
|
||||
public static getStatus(gid: string): {
|
||||
completedLength: number;
|
||||
totalLength: number;
|
||||
downloadSpeed: number;
|
||||
folderName: string;
|
||||
} | null {
|
||||
const downloadItem = this.downloads[gid];
|
||||
if (downloadItem) {
|
||||
return {
|
||||
completedLength: downloadItem.getReceivedBytes(),
|
||||
totalLength: downloadItem.getTotalBytes(),
|
||||
downloadSpeed: downloadItem.getCurrentBytesPerSecond(),
|
||||
folderName: downloadItem.getFilename(),
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static disconnect() {
|
||||
if (this.aria2c) {
|
||||
this.aria2c.kill();
|
||||
this.connected = false;
|
||||
}
|
||||
}
|
||||
|
||||
static async cancelDownload(gid: string) {
|
||||
await this.aria2.call("forceRemove", gid);
|
||||
const downloadItem = this.downloads[gid];
|
||||
downloadItem?.cancel();
|
||||
delete this.downloads[gid];
|
||||
}
|
||||
|
||||
static async pauseDownload(gid: string) {
|
||||
await this.aria2.call("forcePause", gid);
|
||||
const downloadItem = this.downloads[gid];
|
||||
downloadItem?.pause();
|
||||
}
|
||||
|
||||
static async resumeDownload(gid: string) {
|
||||
await this.aria2.call("unpause", gid);
|
||||
const downloadItem = this.downloads[gid];
|
||||
downloadItem?.resume();
|
||||
}
|
||||
|
||||
static async startDownload(downloadPath: string, downloadUrl: string) {
|
||||
if (!this.connected) await this.connect();
|
||||
static async startDownload(
|
||||
downloadPath: string,
|
||||
downloadUrl: string,
|
||||
headers?: Record<string, string>
|
||||
) {
|
||||
return new Promise<string>((resolve) => {
|
||||
const options = headers ? { headers } : {};
|
||||
WindowManager.mainWindow?.webContents.downloadURL(downloadUrl, options);
|
||||
|
||||
const options = {
|
||||
dir: downloadPath,
|
||||
};
|
||||
const gid = ++this.id;
|
||||
|
||||
return this.aria2.call("addUri", [downloadUrl], options);
|
||||
WindowManager.mainWindow?.webContents.session.on(
|
||||
"will-download",
|
||||
(_event, item, _webContents) => {
|
||||
this.downloads[gid.toString()] = item;
|
||||
|
||||
// Set the save path, making Electron not to prompt a save dialog.
|
||||
item.setSavePath(path.join(downloadPath, item.getFilename()));
|
||||
|
||||
resolve(gid.toString());
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,22 +13,36 @@ export class RealDebridDownloader {
|
||||
|
||||
private static async getRealDebridDownloadUrl() {
|
||||
if (this.realDebridTorrentId) {
|
||||
const torrentInfo = await RealDebridClient.getTorrentInfo(
|
||||
let torrentInfo = await RealDebridClient.getTorrentInfo(
|
||||
this.realDebridTorrentId
|
||||
);
|
||||
|
||||
const { status, links } = torrentInfo;
|
||||
|
||||
if (status === "waiting_files_selection") {
|
||||
if (torrentInfo.status === "waiting_files_selection") {
|
||||
await RealDebridClient.selectAllFiles(this.realDebridTorrentId);
|
||||
return null;
|
||||
|
||||
torrentInfo = await RealDebridClient.getTorrentInfo(
|
||||
this.realDebridTorrentId
|
||||
);
|
||||
}
|
||||
|
||||
const { links, status } = torrentInfo;
|
||||
|
||||
if (status === "downloaded") {
|
||||
const [link] = links;
|
||||
|
||||
const { download } = await RealDebridClient.unrestrictLink(link);
|
||||
return decodeURIComponent(download);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.downloadingGame?.uri) {
|
||||
const { download } = await RealDebridClient.unrestrictLink(
|
||||
this.downloadingGame?.uri
|
||||
);
|
||||
|
||||
return decodeURIComponent(download);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -37,7 +51,7 @@ export class RealDebridDownloader {
|
||||
public static async getStatus() {
|
||||
if (this.downloadingGame) {
|
||||
const gid = this.downloads.get(this.downloadingGame.id)!;
|
||||
const status = await HttpDownload.getStatus(gid);
|
||||
const status = HttpDownload.getStatus(gid);
|
||||
|
||||
if (status) {
|
||||
const progress =
|
||||
@@ -50,6 +64,7 @@ export class RealDebridDownloader {
|
||||
fileSize: Number(status.totalLength),
|
||||
progress,
|
||||
status: "active",
|
||||
folderName: status.folderName,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -78,40 +93,15 @@ export class RealDebridDownloader {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.realDebridTorrentId && this.downloadingGame) {
|
||||
const torrentInfo = await RealDebridClient.getTorrentInfo(
|
||||
this.realDebridTorrentId
|
||||
);
|
||||
|
||||
const { status } = torrentInfo;
|
||||
|
||||
if (status === "downloaded") {
|
||||
this.startDownload(this.downloadingGame);
|
||||
}
|
||||
|
||||
const progress = torrentInfo.progress / 100;
|
||||
const totalDownloaded = progress * torrentInfo.bytes;
|
||||
|
||||
return {
|
||||
numPeers: 0,
|
||||
numSeeds: torrentInfo.seeders,
|
||||
downloadSpeed: torrentInfo.speed,
|
||||
timeRemaining: calculateETA(
|
||||
torrentInfo.bytes,
|
||||
totalDownloaded,
|
||||
torrentInfo.speed
|
||||
),
|
||||
isDownloadingMetadata: status === "magnet_conversion",
|
||||
} as DownloadProgress;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static async pauseDownload() {
|
||||
const gid = this.downloads.get(this.downloadingGame!.id!);
|
||||
if (gid) {
|
||||
await HttpDownload.pauseDownload(gid);
|
||||
if (this.downloadingGame) {
|
||||
const gid = this.downloads.get(this.downloadingGame.id);
|
||||
if (gid) {
|
||||
await HttpDownload.pauseDownload(gid);
|
||||
}
|
||||
}
|
||||
|
||||
this.realDebridTorrentId = null;
|
||||
@@ -119,15 +109,19 @@ export class RealDebridDownloader {
|
||||
}
|
||||
|
||||
static async startDownload(game: Game) {
|
||||
this.downloadingGame = game;
|
||||
|
||||
if (this.downloads.has(game.id)) {
|
||||
await this.resumeDownload(game.id!);
|
||||
|
||||
this.downloadingGame = game;
|
||||
return;
|
||||
}
|
||||
|
||||
this.realDebridTorrentId = await RealDebridClient.getTorrentId(game!.uri!);
|
||||
if (game.uri?.startsWith("magnet:")) {
|
||||
this.realDebridTorrentId = await RealDebridClient.getTorrentId(
|
||||
game!.uri!
|
||||
);
|
||||
}
|
||||
|
||||
this.downloadingGame = game;
|
||||
|
||||
const downloadUrl = await this.getRealDebridDownloadUrl();
|
||||
|
||||
@@ -150,6 +144,9 @@ export class RealDebridDownloader {
|
||||
await HttpDownload.cancelDownload(gid);
|
||||
this.downloads.delete(gameId);
|
||||
}
|
||||
|
||||
this.realDebridTorrentId = null;
|
||||
this.downloadingGame = null;
|
||||
}
|
||||
|
||||
static async resumeDownload(gameId: number) {
|
||||
|
||||
61
src/main/services/hosters/gofile.ts
Normal file
61
src/main/services/hosters/gofile.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import axios from "axios";
|
||||
|
||||
export interface GofileAccountsReponse {
|
||||
id: string;
|
||||
token: string;
|
||||
}
|
||||
|
||||
export interface GofileContentChild {
|
||||
id: string;
|
||||
link: string;
|
||||
}
|
||||
|
||||
export interface GofileContentsResponse {
|
||||
id: string;
|
||||
type: string;
|
||||
children: Record<string, GofileContentChild>;
|
||||
}
|
||||
|
||||
export class GofileApi {
|
||||
private static token: string;
|
||||
|
||||
public static async authorize() {
|
||||
const response = await axios.post<{
|
||||
status: string;
|
||||
data: GofileAccountsReponse;
|
||||
}>("https://api.gofile.io/accounts");
|
||||
|
||||
if (response.data.status === "ok") {
|
||||
this.token = response.data.data.token;
|
||||
return this.token;
|
||||
}
|
||||
|
||||
throw new Error("Failed to authorize");
|
||||
}
|
||||
|
||||
public static async getDownloadLink(id: string) {
|
||||
const searchParams = new URLSearchParams({
|
||||
wt: "4fd6sg89d7s6",
|
||||
});
|
||||
|
||||
const response = await axios.get<{
|
||||
status: string;
|
||||
data: GofileContentsResponse;
|
||||
}>(`https://api.gofile.io/contents/${id}?${searchParams.toString()}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${this.token}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (response.data.status === "ok") {
|
||||
if (response.data.data.type !== "folder") {
|
||||
throw new Error("Only folders are supported");
|
||||
}
|
||||
|
||||
const [firstChild] = Object.values(response.data.data.children);
|
||||
return firstChild.link;
|
||||
}
|
||||
|
||||
throw new Error("Failed to get download link");
|
||||
}
|
||||
}
|
||||
1
src/main/services/hosters/index.ts
Normal file
1
src/main/services/hosters/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./gofile";
|
||||
@@ -64,6 +64,14 @@ export class HydraApi {
|
||||
}
|
||||
}
|
||||
|
||||
static handleSignOut() {
|
||||
this.userAuth = {
|
||||
authToken: "",
|
||||
refreshToken: "",
|
||||
expirationTimestamp: 0,
|
||||
};
|
||||
}
|
||||
|
||||
static async setupApi() {
|
||||
this.instance = axios.create({
|
||||
baseURL: import.meta.env.MAIN_VITE_API_URL,
|
||||
|
||||
@@ -46,7 +46,7 @@ export class RealDebridClient {
|
||||
static async selectAllFiles(id: string) {
|
||||
const searchParams = new URLSearchParams({ files: "all" });
|
||||
|
||||
await this.instance.post(
|
||||
return this.instance.post(
|
||||
`/torrents/selectFiles/${id}`,
|
||||
searchParams.toString()
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user