diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index ee9fb16b..417df3b7 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -34,8 +34,8 @@ "bottom_panel": { "no_downloads_in_progress": "No downloads in progress", "downloading_metadata": "Downloading {{title}} metadata…", - "checking_files": "Checking {{title}} files… ({{percentage}} complete)", - "downloading": "Downloading {{title}}… ({{percentage}} complete) - Conclusion {{eta}} - {{speed}}" + "downloading": "Downloading {{title}}… ({{percentage}} complete) - Conclusion {{eta}} - {{speed}}", + "calculating_eta": "Downloading {{title}}… ({{percentage}} complete) - Calculating remaining time…" }, "catalogue": { "next_page": "Next page", @@ -55,15 +55,15 @@ "remove_from_list": "Remove", "space_left_on_disk": "{{space}} left on disk", "eta": "Conclusion {{eta}}", + "calculating_eta": "Calculating remaining time…", "downloading_metadata": "Downloading metadata…", - "checking_files": "Checking files…", "filter": "Filter repacks", "requirements": "System requirements", "minimum": "Minimum", "recommended": "Recommended", "no_minimum_requirements": "{{title}} doesn't provide minimum requirements information", "no_recommended_requirements": "{{title}} doesn't provide recommended requirements information", - "paused_progress": "{{progress}} (Paused)", + "paused": "Paused", "release_date": "Released on {{date}}", "publisher": "Published by {{publisher}}", "copy_link_to_clipboard": "Copy link", @@ -126,7 +126,6 @@ "filter": "Filter downloaded games", "remove": "Remove", "downloading_metadata": "Downloading metadata…", - "checking_files": "Checking files…", "starting_download": "Starting download…", "deleting": "Deleting installer…", "delete": "Remove installer", diff --git a/src/main/events/index.ts b/src/main/events/index.ts index debca0e4..70f483d5 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -30,6 +30,7 @@ import "./user-preferences/auto-launch"; import "./autoupdater/check-for-updates"; import "./autoupdater/restart-and-install-update"; import "./autoupdater/continue-to-main-window"; +import "./user-preferences/authenticate-real-debrid"; ipcMain.handle("ping", () => "pong"); ipcMain.handle("getVersion", () => app.getVersion()); diff --git a/src/main/events/user-preferences/authenticate-real-debrid.ts b/src/main/events/user-preferences/authenticate-real-debrid.ts new file mode 100644 index 00000000..01705db7 --- /dev/null +++ b/src/main/events/user-preferences/authenticate-real-debrid.ts @@ -0,0 +1,14 @@ +import { RealDebridClient } from "@main/services/real-debrid"; +import { registerEvent } from "../register-event"; + +const authenticateRealDebrid = async ( + _event: Electron.IpcMainInvokeEvent, + apiToken: string +) => { + RealDebridClient.authorize(apiToken); + + const user = await RealDebridClient.getUser(); + return user; +}; + +registerEvent("authenticateRealDebrid", authenticateRealDebrid); diff --git a/src/main/main.ts b/src/main/main.ts index 50f18202..63162bbf 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -86,7 +86,7 @@ const loadState = async (userPreferences: UserPreferences | null) => { import("./events"); if (userPreferences?.realDebridApiToken) - await RealDebridClient.authorize(userPreferences?.realDebridApiToken); + RealDebridClient.authorize(userPreferences?.realDebridApiToken); const game = await gameRepository.findOne({ where: { diff --git a/src/main/services/download-manager.ts b/src/main/services/download-manager.ts index 3511dde5..b14e0b14 100644 --- a/src/main/services/download-manager.ts +++ b/src/main/services/download-manager.ts @@ -92,7 +92,7 @@ export class DownloadManager { const status = await this.aria2.call("tellStatus", this.gid); - const downloadingMetadata = status.bittorrent && !status.bittorrent?.info; + const isDownloadingMetadata = status.bittorrent && !status.bittorrent?.info; if (status.followedBy?.length) { this.gid = status.followedBy[0]; @@ -103,7 +103,7 @@ export class DownloadManager { const progress = Number(status.completedLength) / Number(status.totalLength); - if (!downloadingMetadata) { + if (!isDownloadingMetadata) { const update: QueryDeepPartialEntity = { bytesDownloaded: Number(status.completedLength), fileSize: Number(status.totalLength), @@ -127,7 +127,7 @@ export class DownloadManager { relations: { repack: true }, }); - if (progress === 1 && game && !downloadingMetadata) { + if (progress === 1 && game && !isDownloadingMetadata) { await this.publishNotification(); /* Only cancel bittorrent downloads to stop seeding @@ -150,7 +150,7 @@ export class DownloadManager { numSeeds: Number(status.numSeeders ?? 0), downloadSpeed: Number(status.downloadSpeed), timeRemaining: this.getETA(status), - downloadingMetadata: !!downloadingMetadata, + isDownloadingMetadata: !!isDownloadingMetadata, game, } as DownloadProgress; diff --git a/src/main/services/real-debrid.ts b/src/main/services/real-debrid.ts index 355a59b3..bf22e5ae 100644 --- a/src/main/services/real-debrid.ts +++ b/src/main/services/real-debrid.ts @@ -1,10 +1,11 @@ import { Game } from "@main/entity"; +import axios, { AxiosInstance } from "axios"; import type { RealDebridAddMagnet, RealDebridTorrentInfo, RealDebridUnrestrictLink, -} from "./real-debrid.types"; -import axios, { AxiosInstance } from "axios"; + RealDebridUser, +} from "@types"; const base = "https://api.real-debrid.com/rest/1.0"; @@ -29,6 +30,11 @@ export class RealDebridClient { return response.data; } + static async getUser() { + const response = await this.instance.get(`/user`); + return response.data; + } + static async selectAllFiles(id: string) { const searchParams = new URLSearchParams({ files: "all" }); @@ -65,30 +71,29 @@ export class RealDebridClient { const hash = RealDebridClient.extractSHA1FromMagnet(game!.repack.magnet); let torrent = torrents.find((t) => t.hash === hash); + // User haven't downloaded this torrent yet if (!torrent) { const magnet = await RealDebridClient.addMagnet(game!.repack.magnet); - if (magnet && magnet.id) { + if (magnet) { await RealDebridClient.selectAllFiles(magnet.id); torrent = await RealDebridClient.getInfo(magnet.id); + + const { links } = torrent; + const { download } = await RealDebridClient.unrestrictLink(links[0]); + + if (!download) { + throw new Error("Torrent not cached on Real Debrid"); + } + + return download; } } - 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) { + static authorize(apiToken: string) { this.instance = axios.create({ baseURL: base, headers: { diff --git a/src/main/services/real-debrid.types.ts b/src/main/services/real-debrid.types.ts deleted file mode 100644 index 6707641f..00000000 --- a/src/main/services/real-debrid.types.ts +++ /dev/null @@ -1,51 +0,0 @@ -export interface RealDebridUnrestrictLink { - id: string; - filename: string; - mimeType: string; - filesize: number; - link: string; - host: string; - host_icon: string; - chunks: number; - crc: number; - download: string; - streamable: number; -} - -export interface RealDebridAddMagnet { - id: string; - // URL of the created ressource - uri: string; -} - -export interface RealDebridTorrentInfo { - id: string; - filename: string; - original_filename: string; // Original name of the torrent - hash: string; // SHA1 Hash of the torrent - bytes: number; // Size of selected files only - original_bytes: number; // Total size of the torrent - host: string; // Host main domain - split: number; // Split size of links - progress: number; // Possible values: 0 to 100 - status: string; // Current status of the torrent: magnet_error, magnet_conversion, waiting_files_selection, queued, downloading, downloaded, error, virus, compressing, uploading, dead - added: string; // jsonDate - files: [ - { - id: number; - path: string; // Path to the file inside the torrent, starting with "/" - bytes: number; - selected: number; // 0 or 1 - }, - { - id: number; - path: string; // Path to the file inside the torrent, starting with "/" - bytes: number; - selected: number; // 0 or 1 - }, - ]; - links: string[]; - ended: string; // !! Only present when finished, jsonDate - speed: number; // !! Only present in "downloading", "compressing", "uploading" status - seeders: number; // !! Only present in "downloading", "magnet_conversion" status -} diff --git a/src/preload/index.ts b/src/preload/index.ts index ecd328df..57ddc43b 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -49,6 +49,8 @@ contextBridge.exposeInMainWorld("electron", { updateUserPreferences: (preferences: UserPreferences) => ipcRenderer.invoke("updateUserPreferences", preferences), autoLaunch: (enabled: boolean) => ipcRenderer.invoke("autoLaunch", enabled), + authenticateRealDebrid: (apiToken: string) => + ipcRenderer.invoke("authenticateRealDebrid", apiToken), /* Library */ addGameToLibrary: ( diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx index 3a10b98c..28abbd71 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useRef } from "react"; -import { Sidebar, BottomPanel, Header } from "@renderer/components"; +import { Sidebar, BottomPanel, Header, Toast } from "@renderer/components"; import { useAppDispatch, @@ -18,6 +18,7 @@ import { clearSearch, setUserPreferences, toggleDraggingDisabled, + closeToast, } from "@renderer/features"; document.body.classList.add(themeClass); @@ -41,6 +42,7 @@ export function App() { const draggingDisabled = useAppSelector( (state) => state.window.draggingDisabled ); + const toast = useAppSelector((state) => state.toast); useEffect(() => { Promise.all([window.electron.getUserPreferences(), updateLibrary()]).then( @@ -108,6 +110,10 @@ export function App() { }); }, [dispatch, draggingDisabled]); + const handleToastClose = useCallback(() => { + dispatch(closeToast()); + }, [dispatch]); + return ( <> {window.electron.platform === "win32" && ( @@ -128,6 +134,13 @@ export function App() {
+ +
diff --git a/src/renderer/src/assets/epic-games-logo.svg b/src/renderer/src/assets/epic-games-logo.svg deleted file mode 100644 index a6c53dbc..00000000 --- a/src/renderer/src/assets/epic-games-logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/renderer/src/assets/telegram-icon.svg b/src/renderer/src/assets/telegram-icon.svg deleted file mode 100644 index 962ab45f..00000000 --- a/src/renderer/src/assets/telegram-icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/renderer/src/assets/x-icon.svg b/src/renderer/src/assets/x-icon.svg deleted file mode 100644 index c394d154..00000000 --- a/src/renderer/src/assets/x-icon.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/renderer/src/components/bottom-panel/bottom-panel.css.ts b/src/renderer/src/components/bottom-panel/bottom-panel.css.ts index f339e0d5..39aef69f 100644 --- a/src/renderer/src/components/bottom-panel/bottom-panel.css.ts +++ b/src/renderer/src/components/bottom-panel/bottom-panel.css.ts @@ -9,6 +9,7 @@ export const bottomPanel = style({ alignItems: "center", transition: "all ease 0.2s", justifyContent: "space-between", + position: "relative", zIndex: "1", }); diff --git a/src/renderer/src/components/bottom-panel/bottom-panel.tsx b/src/renderer/src/components/bottom-panel/bottom-panel.tsx index e52cc3b0..db654c8a 100644 --- a/src/renderer/src/components/bottom-panel/bottom-panel.tsx +++ b/src/renderer/src/components/bottom-panel/bottom-panel.tsx @@ -1,10 +1,10 @@ +import { useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { useDownload } from "@renderer/hooks"; import * as styles from "./bottom-panel.css"; -import { vars } from "../../theme.css"; -import { useEffect, useMemo, useState } from "react"; + import { useNavigate } from "react-router-dom"; import { VERSION_CODENAME } from "@renderer/constants"; @@ -25,6 +25,16 @@ export function BottomPanel() { const status = useMemo(() => { if (isGameDownloading) { + if (lastPacket?.isDownloadingMetadata) + return t("downloading_metadata", { title: lastPacket?.game.title }); + + if (!eta) { + return t("calculating_eta", { + title: lastPacket?.game.title, + percentage: progress, + }); + } + return t("downloading", { title: lastPacket?.game.title, percentage: progress, @@ -34,17 +44,18 @@ export function BottomPanel() { } return t("no_downloads_in_progress"); - }, [t, isGameDownloading, lastPacket?.game, progress, eta, downloadSpeed]); + }, [ + t, + isGameDownloading, + lastPacket?.game, + lastPacket?.isDownloadingMetadata, + progress, + eta, + downloadSpeed, + ]); return ( -