fix: fixing seeding on level

This commit is contained in:
Chubby Granny Chaser
2025-02-03 13:57:03 +00:00
231 changed files with 5236 additions and 4761 deletions

View File

@@ -1,6 +1,5 @@
import { registerEvent } from "../register-event";
import { DownloadManager, HydraApi, gamesPlaytime } from "@main/services";
import { PythonRPC } from "@main/services/python-rpc";
import { db, downloadsSublevel, gamesSublevel, levelKeys } from "@main/level";
const signOut = async (_event: Electron.IpcMainInvokeEvent) => {
@@ -25,9 +24,6 @@ const signOut = async (_event: Electron.IpcMainInvokeEvent) => {
/* Cancels any ongoing downloads */
DownloadManager.cancelDownload();
/* Disconnects libtorrent */
PythonRPC.kill();
HydraApi.handleSignOut();
await Promise.all([

View File

@@ -46,6 +46,7 @@ import "./user-preferences/auto-launch";
import "./autoupdater/check-for-updates";
import "./autoupdater/restart-and-install-update";
import "./user-preferences/authenticate-real-debrid";
import "./user-preferences/authenticate-torbox";
import "./download-sources/put-download-source";
import "./auth/sign-out";
import "./auth/open-auth-window";

View File

@@ -46,9 +46,9 @@ const addGameToLibrary = async (
await gamesSublevel.put(levelKeys.game(shop, objectId), game);
updateLocalUnlockedAchivements(game!);
updateLocalUnlockedAchivements(game);
createGame(game!).catch(() => {});
createGame(game).catch(() => {});
}
};

View File

@@ -76,10 +76,10 @@ const startGameDownload = async (
queued: true,
};
await downloadsSublevel.put(gameKey, download);
try {
await DownloadManager.startDownload(download);
await DownloadManager.startDownload(download).then(() => {
return downloadsSublevel.put(gameKey, download);
});
const updatedGame = await gamesSublevel.get(gameKey);
@@ -113,6 +113,10 @@ const startGameDownload = async (
error: DownloadError.RealDebridAccountNotAuthorized,
};
}
if (downloader === Downloader.TorBox) {
return { ok: false, error: err.response?.data?.detail };
}
}
if (err instanceof Error) {

View File

@@ -0,0 +1,14 @@
import { registerEvent } from "../register-event";
import { TorBoxClient } from "@main/services/download/torbox";
const authenticateTorBox = async (
_event: Electron.IpcMainInvokeEvent,
apiToken: string
) => {
TorBoxClient.authorize(apiToken);
const user = await TorBoxClient.getUser();
return user;
};
registerEvent("authenticateTorBox", authenticateTorBox);

View File

@@ -15,6 +15,12 @@ const getUserPreferences = async () =>
);
}
if (userPreferences?.torBoxApiToken) {
userPreferences.torBoxApiToken = Crypto.decrypt(
userPreferences.torBoxApiToken
);
}
return userPreferences;
});

View File

@@ -30,6 +30,10 @@ const updateUserPreferences = async (
);
}
if (preferences.torBoxApiToken) {
preferences.torBoxApiToken = Crypto.encrypt(preferences.torBoxApiToken);
}
if (!preferences.downloadsPath) {
preferences.downloadsPath = null;
}

View File

@@ -1,3 +1,2 @@
export { db } from "./level";
export * from "./sublevels";

View File

@@ -2,5 +2,4 @@ export * from "./downloads";
export * from "./games";
export * from "./game-shop-cache";
export * from "./game-achievements";
export * from "./keys";

View File

@@ -20,6 +20,7 @@ import {
} from "./level";
import { Auth, User, type UserPreferences } from "@types";
import { knexClient } from "./knex-client";
import { TorBoxClient } from "./services/download/torbox";
export const loadState = async () => {
const userPreferences = await migrateFromSqlite().then(async () => {
@@ -42,6 +43,10 @@ export const loadState = async () => {
);
}
if (userPreferences?.torBoxApiToken) {
TorBoxClient.authorize(Crypto.decrypt(userPreferences.torBoxApiToken));
}
Ludusavi.addManifestToLudusaviConfig();
HydraApi.setupApi().then(() => {

View File

@@ -15,6 +15,7 @@ import path from "path";
import { logger } from "../logger";
import { db, downloadsSublevel, gamesSublevel, levelKeys } from "@main/level";
import { sortBy } from "lodash-es";
import { TorBoxClient } from "./torbox";
export class DownloadManager {
private static downloadingGameId: string | null = null;
@@ -233,7 +234,9 @@ export class DownloadManager {
});
WindowManager.mainWindow?.setProgressBar(-1);
if (downloadKey === this.downloadingGameId) {
WindowManager.mainWindow?.webContents.send("on-download-progress", null);
this.downloadingGameId = null;
}
}
@@ -275,6 +278,7 @@ export class DownloadManager {
}
case Downloader.PixelDrain: {
const id = download.uri.split("/").pop();
return {
action: "start",
game_id: downloadId,
@@ -329,6 +333,18 @@ export class DownloadManager {
save_path: download.downloadPath,
};
}
case Downloader.TorBox: {
const { name, url } = await TorBoxClient.getDownloadInfo(download.uri);
if (!url) return;
return {
action: "start",
game_id: downloadId,
url,
save_path: download.downloadPath,
out: name,
};
}
}
}

View File

@@ -6,24 +6,23 @@ import type {
TorBoxAddTorrentRequest,
TorBoxRequestLinkRequest,
} from "@types";
import { logger } from "../logger";
export class TorBoxClient {
private static instance: AxiosInstance;
private static readonly baseURL = "https://api.torbox.app/v1/api";
public static apiToken: string;
private static apiToken: string;
static authorize(apiToken: string) {
this.apiToken = apiToken;
this.instance = axios.create({
baseURL: this.baseURL,
headers: {
Authorization: `Bearer ${apiToken}`,
},
});
this.apiToken = apiToken;
}
static async addMagnet(magnet: string) {
private static async addMagnet(magnet: string) {
const form = new FormData();
form.append("magnet", magnet);
@@ -32,6 +31,10 @@ export class TorBoxClient {
form
);
if (!response.data.success) {
throw new Error(response.data.detail);
}
return response.data.data;
}
@@ -55,22 +58,16 @@ export class TorBoxClient {
}
static async requestLink(id: number) {
const searchParams = new URLSearchParams({});
searchParams.set("token", this.apiToken);
searchParams.set("torrent_id", id.toString());
searchParams.set("zip_link", "true");
const searchParams = new URLSearchParams({
token: this.apiToken,
torrent_id: id.toString(),
zip_link: "true",
});
const response = await this.instance.get<TorBoxRequestLinkRequest>(
"/torrents/requestdl?" + searchParams.toString()
);
if (response.status !== 200) {
logger.error(response.data.error);
logger.error(response.data.detail);
return null;
}
return response.data.data;
}
@@ -81,7 +78,7 @@ export class TorBoxClient {
return response.data.data;
}
static async getTorrentId(magnetUri: string) {
private static async getTorrentIdAndName(magnetUri: string) {
const userTorrents = await this.getAllTorrentsFromUser();
const { infoHash } = await parseTorrent(magnetUri);
@@ -89,9 +86,18 @@ export class TorBoxClient {
(userTorrent) => userTorrent.hash === infoHash
);
if (userTorrent) return userTorrent.id;
if (userTorrent) return { id: userTorrent.id, name: userTorrent.name };
const torrent = await this.addMagnet(magnetUri);
return torrent.torrent_id;
return { id: torrent.torrent_id, name: torrent.name };
}
static async getDownloadInfo(uri: string) {
const torrentData = await this.getTorrentIdAndName(uri);
const url = await this.requestLink(torrentData.id);
const name = torrentData.name ? `${torrentData.name}.zip` : undefined;
return { url, name };
}
}

View File

@@ -35,22 +35,20 @@ export const mergeWithRemoteGames = async () => {
name: "getById",
});
if (steamGame) {
const iconUrl = steamGame?.clientIcon
? steamUrlBuilder.icon(game.objectId, steamGame.clientIcon)
: null;
const iconUrl = steamGame?.clientIcon
? steamUrlBuilder.icon(game.objectId, steamGame.clientIcon)
: null;
gamesSublevel.put(levelKeys.game(game.shop, game.objectId), {
objectId: game.objectId,
title: steamGame?.name,
remoteId: game.id,
shop: game.shop,
iconUrl,
lastTimePlayed: game.lastTimePlayed,
playTimeInMilliseconds: game.playTimeInMilliseconds,
isDeleted: false,
});
}
gamesSublevel.put(levelKeys.game(game.shop, game.objectId), {
objectId: game.objectId,
title: steamGame?.name,
remoteId: game.id,
shop: game.shop,
iconUrl,
lastTimePlayed: game.lastTimePlayed,
playTimeInMilliseconds: game.playTimeInMilliseconds,
isDeleted: false,
});
}
}
})

View File

@@ -15,7 +15,7 @@ export const uploadGamesBatch = async () => {
);
});
const gamesChunks = chunk(games, 200);
const gamesChunks = chunk(games, 50);
for (const chunk of gamesChunks) {
await HydraApi.post(

View File

@@ -21,11 +21,18 @@ export const getSteamAppDetails = async (
});
return axios
.get(
.get<SteamAppDetailsResponse>(
`http://store.steampowered.com/api/appdetails?${searchParams.toString()}`
)
.then((response) => {
if (response.data[objectId].success) return response.data[objectId].data;
if (response.data[objectId].success) {
const data = response.data[objectId].data;
return {
...data,
objectId,
};
}
return null;
})
.catch((err) => {