diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 167eb0be..0674d1b5 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -146,7 +146,8 @@ "behavior": "Behavior", "enable_real_debrid": "Enable Real Debrid", "real_debrid": "Real Debrid", - "real_debrid_api_token_hint": "You can get your API key <0>here" + "real_debrid_api_token_hint": "You can get your API key <0>here.", + "save_changes": "Save changes" }, "notifications": { "download_complete": "Download complete", diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json index 2ee0d827..dda53065 100644 --- a/src/locales/pt/translation.json +++ b/src/locales/pt/translation.json @@ -133,9 +133,14 @@ "enable_repack_list_notifications": "Quando a lista de repacks for atualizada", "telemetry": "Telemetria", "telemetry_description": "Habilitar estatísticas de uso anônimas", - "behavior": "Comportamento", "quit_app_instead_hiding": "Fechar o aplicativo em vez de minimizá-lo", - "launch_with_system": "Iniciar aplicativo na inicialização do sistema" + "launch_with_system": "Iniciar aplicativo na inicialização do sistema", + "general": "Geral", + "behavior": "Comportamento", + "enable_real_debrid": "Habilitar Real Debrid", + "real_debrid": "Real Debrid", + "real_debrid_api_token_hint": "Você pode obter sua chave de API <0>aqui.", + "save_changes": "Salvar mudanças" }, "notifications": { "download_complete": "Download concluído", diff --git a/src/main/constants.ts b/src/main/constants.ts index 6c61f0b5..a229cb31 100644 --- a/src/main/constants.ts +++ b/src/main/constants.ts @@ -41,7 +41,5 @@ export const databasePath = path.join( "hydra.db" ); -console.log(databasePath); - export const INSTALLATION_ID_LENGTH = 6; export const ACTIVATION_KEY_MULTIPLIER = 7; diff --git a/src/main/events/helpers/generate-lutris-yaml.ts b/src/main/events/helpers/generate-lutris-yaml.ts index 75c9786b..f47a2a68 100644 --- a/src/main/events/helpers/generate-lutris-yaml.ts +++ b/src/main/events/helpers/generate-lutris-yaml.ts @@ -28,8 +28,8 @@ export const generateYML = (game: Game) => { { task: { executable: path.join( - game.downloadPath, - game.folderName, + game.downloadPath!, + game.folderName!, "setup.exe" ), name: "wineexec", diff --git a/src/main/events/library/open-game-installer.ts b/src/main/events/library/open-game-installer.ts index 27f110e5..2621d1f1 100644 --- a/src/main/events/library/open-game-installer.ts +++ b/src/main/events/library/open-game-installer.ts @@ -21,7 +21,7 @@ const openGameInstaller = async ( const gamePath = path.join( game.downloadPath ?? (await getDownloadsPath()), - game.folderName + game.folderName! ); if (!fs.existsSync(gamePath)) { diff --git a/src/main/services/downloaders/real-debrid.downloader.ts b/src/main/services/downloaders/real-debrid.downloader.ts index 38b27931..4c8e2691 100644 --- a/src/main/services/downloaders/real-debrid.downloader.ts +++ b/src/main/services/downloaders/real-debrid.downloader.ts @@ -38,12 +38,9 @@ export class RealDebridDownloader extends Downloader { const updatePayload: QueryDeepPartialEntity = { status: GameStatus.Finished, - progress: 1, }; - await this.updateGameProgress(game.id, updatePayload, { - timeRemaining: 0, - }); + await this.updateGameProgress(game.id, updatePayload, {}); } static destroy() { @@ -100,7 +97,7 @@ export class RealDebridDownloader extends Downloader { this.download.on("end", async () => { const updatePayload: QueryDeepPartialEntity = { status: GameStatus.Decompressing, - progress: 0.99, + progress: 1, }; await this.updateGameProgress(game.id, updatePayload, { diff --git a/src/main/services/real-debrid.ts b/src/main/services/real-debrid.ts index 7fa12e86..44798062 100644 --- a/src/main/services/real-debrid.ts +++ b/src/main/services/real-debrid.ts @@ -1,3 +1,4 @@ +import { Game } from "@main/entity"; import type { RealDebridAddMagnet, RealDebridTorrentInfo, diff --git a/src/main/services/repack-tracker/1337x.ts b/src/main/services/repack-tracker/1337x.ts index 8573079b..5e6ae527 100644 --- a/src/main/services/repack-tracker/1337x.ts +++ b/src/main/services/repack-tracker/1337x.ts @@ -33,9 +33,9 @@ const getTorrentDetails = async (path: string) => { return { magnet: $a?.href, - fileSize: $totalSize.querySelector("span").textContent ?? undefined, + fileSize: $totalSize.querySelector("span")!.textContent, uploadDate: formatUploadDate( - $dateUploaded.querySelector("span").textContent! + $dateUploaded.querySelector("span")!.textContent! ), }; }; @@ -65,8 +65,7 @@ export const getTorrentListLastPage = async (user: string) => { export const extractTorrentsFromDocument = async ( page: number, user: string, - document: Document, - existingRepacks: Repack[] = [] + document: Document ) => { const $trs = Array.from(document.querySelectorAll("tbody tr")); @@ -78,24 +77,13 @@ export const extractTorrentsFromDocument = async ( const url = $name.href; const title = $name.textContent ?? ""; - if (existingRepacks.some((repack) => repack.title === title)) { - return { - title, - magnet: "", - fileSize: null, - uploadDate: null, - repacker: user, - page, - }; - } - const details = await getTorrentDetails(url); return { title, magnet: details.magnet, - fileSize: details.fileSize ?? null, - uploadDate: details.uploadDate ?? null, + fileSize: details.fileSize ?? "N/A", + uploadDate: details.uploadDate ?? new Date(), repacker: user, page, }; @@ -114,13 +102,11 @@ export const getNewRepacksFromUser = async ( const repacks = await extractTorrentsFromDocument( page, user, - window.document, - existingRepacks + window.document ); const newRepacks = repacks.filter( (repack) => - repack.uploadDate && !existingRepacks.some( (existingRepack) => existingRepack.title === repack.title ) diff --git a/src/main/services/repack-tracker/cpg-repacks.ts b/src/main/services/repack-tracker/cpg-repacks.ts index 2b939d08..d1ba6cc4 100644 --- a/src/main/services/repack-tracker/cpg-repacks.ts +++ b/src/main/services/repack-tracker/cpg-repacks.ts @@ -4,6 +4,7 @@ import { Repack } from "@main/entity"; import { requestWebPage, savePage } from "./helpers"; import { logger } from "../logger"; +import type { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity"; export const getNewRepacksFromCPG = async ( existingRepacks: Repack[] = [], @@ -13,11 +14,11 @@ export const getNewRepacksFromCPG = async ( const { window } = new JSDOM(data); - const repacks = []; + const repacks: QueryDeepPartialEntity[] = []; try { Array.from(window.document.querySelectorAll(".post")).forEach(($post) => { - const $title = $post.querySelector(".entry-title"); + const $title = $post.querySelector(".entry-title")!; const uploadDate = $post.querySelector("time")?.getAttribute("datetime"); const $downloadInfo = Array.from( @@ -31,26 +32,25 @@ export const getNewRepacksFromCPG = async ( $a.textContent?.startsWith("Magent") ); - const fileSize = $downloadInfo.textContent + const fileSize = ($downloadInfo?.textContent ?? "") .split("Download link => ") .at(1); repacks.push({ - title: $title.textContent, + title: $title.textContent!, fileSize: fileSize ?? "N/A", - magnet: $magnet.href, + magnet: $magnet!.href, repacker: "CPG", page, - uploadDate: new Date(uploadDate), + uploadDate: uploadDate ? new Date(uploadDate) : new Date(), }); }); - } catch (err) { - logger.error(err.message, { method: "getNewRepacksFromCPG" }); + } catch (err: unknown) { + logger.error((err as Error).message, { method: "getNewRepacksFromCPG" }); } const newRepacks = repacks.filter( (repack) => - repack.uploadDate && !existingRepacks.some( (existingRepack) => existingRepack.title === repack.title ) diff --git a/src/main/services/repack-tracker/gog.ts b/src/main/services/repack-tracker/gog.ts index 00c78e36..aa22ee5c 100644 --- a/src/main/services/repack-tracker/gog.ts +++ b/src/main/services/repack-tracker/gog.ts @@ -16,14 +16,14 @@ const getGOGGame = async (url: string) => { const $em = window.document.querySelector( "p:not(.lightweight-accordion *) em" - ); - const fileSize = $em.textContent.split("Size: ").at(1); + )!; + const fileSize = $em.textContent!.split("Size: ").at(1); const $downloadButton = window.document.querySelector( ".download-btn:not(.lightweight-accordion *)" ) as HTMLAnchorElement; const { searchParams } = new URL($downloadButton.href); - const magnet = Buffer.from(searchParams.get("url"), "base64").toString( + const magnet = Buffer.from(searchParams.get("url")!, "base64").toString( "utf-8" ); @@ -50,10 +50,10 @@ export const getNewGOGGames = async (existingRepacks: Repack[] = []) => { const $lis = Array.from($ul.querySelectorAll("li")); for (const $li of $lis) { - const $a = $li.querySelector("a"); + const $a = $li.querySelector("a")!; const href = $a.href; - const title = $a.textContent.trim(); + const title = $a.textContent!.trim(); const gameExists = existingRepacks.some( (existingRepack) => existingRepack.title === title diff --git a/src/main/services/repack-tracker/online-fix.ts b/src/main/services/repack-tracker/online-fix.ts index a473679f..e73c6cc6 100644 --- a/src/main/services/repack-tracker/online-fix.ts +++ b/src/main/services/repack-tracker/online-fix.ts @@ -13,6 +13,9 @@ import { ru } from "date-fns/locale"; import { onlinefixFormatter } from "@main/helpers"; import makeFetchCookie from "fetch-cookie"; import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity"; +import { formatBytes } from "@shared"; + +const ONLINE_FIX_URL = "https://online-fix.me/"; export const getNewRepacksFromOnlineFix = async ( existingRepacks: Repack[] = [], @@ -27,14 +30,14 @@ export const getNewRepacksFromOnlineFix = async ( const http = makeFetchCookie(fetch, cookieJar); if (page === 1) { - await http("https://online-fix.me/"); + await http(ONLINE_FIX_URL); const preLogin = ((await http("https://online-fix.me/engine/ajax/authtoken.php", { method: "GET", headers: { "X-Requested-With": "XMLHttpRequest", - Referer: "https://online-fix.me/", + Referer: ONLINE_FIX_URL, }, }).then((res) => res.json())) as { field: string; @@ -50,11 +53,11 @@ export const getNewRepacksFromOnlineFix = async ( [preLogin.field]: preLogin.value, }); - await http("https://online-fix.me/", { + await http(ONLINE_FIX_URL, { method: "POST", headers: { - Referer: "https://online-fix.me", - Origin: "https://online-fix.me", + Referer: ONLINE_FIX_URL, + Origin: ONLINE_FIX_URL, "Content-Type": "application/x-www-form-urlencoded", }, body: params.toString(), @@ -149,13 +152,8 @@ export const getNewRepacksFromOnlineFix = async ( const torrentSizeInBytes = torrent.length; if (!torrentSizeInBytes) return; - const fileSizeFormatted = - torrentSizeInBytes >= 1024 ** 3 - ? `${(torrentSizeInBytes / 1024 ** 3).toFixed(1)}GBs` - : `${(torrentSizeInBytes / 1024 ** 2).toFixed(1)}MBs`; - repacks.push({ - fileSize: fileSizeFormatted, + fileSize: formatBytes(torrentSizeInBytes), magnet: magnetLink, page: 1, repacker: "onlinefix", diff --git a/src/main/services/repack-tracker/xatab.ts b/src/main/services/repack-tracker/xatab.ts index df075e88..1c43327b 100644 --- a/src/main/services/repack-tracker/xatab.ts +++ b/src/main/services/repack-tracker/xatab.ts @@ -7,6 +7,8 @@ import { requestWebPage, savePage } from "./helpers"; import createWorker from "@main/workers/torrent-parser.worker?nodeWorker"; import { toMagnetURI } from "parse-torrent"; import type { Instance } from "parse-torrent"; +import { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity"; +import { formatBytes } from "@shared"; const worker = createWorker({}); @@ -23,10 +25,9 @@ const formatXatabDate = (str: string) => { return date; }; -const formatXatabDownloadSize = (str: string) => - str.replace(",", ".").replace(/Гб/g, "GB").replace(/Мб/g, "MB"); - -const getXatabRepack = (url: string) => { +const getXatabRepack = ( + url: string +): Promise<{ fileSize: string; magnet: string; uploadDate: Date }> => { return new Promise((resolve) => { (async () => { const data = await requestWebPage(url); @@ -34,7 +35,6 @@ const getXatabRepack = (url: string) => { const { document } = window; const $uploadDate = document.querySelector(".entry__date"); - const $size = document.querySelector(".entry__info-size"); const $downloadButton = document.querySelector( ".download-torrent" @@ -42,17 +42,13 @@ const getXatabRepack = (url: string) => { if (!$downloadButton) throw new Error("Download button not found"); - const onMessage = (torrent: Instance) => { + worker.once("message", (torrent: Instance) => { resolve({ - fileSize: formatXatabDownloadSize($size.textContent).toUpperCase(), + fileSize: formatBytes(torrent.length ?? 0), magnet: toMagnetURI(torrent), - uploadDate: formatXatabDate($uploadDate.textContent), + uploadDate: formatXatabDate($uploadDate!.textContent!), }); - - worker.removeListener("message", onMessage); - }; - - worker.once("message", onMessage); + }); })(); }); }; @@ -65,7 +61,7 @@ export const getNewRepacksFromXatab = async ( const { window } = new JSDOM(data); - const repacks = []; + const repacks: QueryDeepPartialEntity[] = []; for (const $a of Array.from( window.document.querySelectorAll(".entry__title a") @@ -74,7 +70,7 @@ export const getNewRepacksFromXatab = async ( const repack = await getXatabRepack(($a as HTMLAnchorElement).href); repacks.push({ - title: $a.textContent, + title: $a.textContent!, repacker: "Xatab", ...repack, page, diff --git a/src/renderer/src/assets/telegram-icon.svg b/src/renderer/src/assets/telegram-icon.svg index 35521851..962ab45f 100644 --- a/src/renderer/src/assets/telegram-icon.svg +++ b/src/renderer/src/assets/telegram-icon.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/renderer/src/assets/x-icon.svg b/src/renderer/src/assets/x-icon.svg index f594427b..c394d154 100644 --- a/src/renderer/src/assets/x-icon.svg +++ b/src/renderer/src/assets/x-icon.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/renderer/src/hooks/use-download.ts b/src/renderer/src/hooks/use-download.ts index f5033738..f0773533 100644 --- a/src/renderer/src/hooks/use-download.ts +++ b/src/renderer/src/hooks/use-download.ts @@ -11,8 +11,7 @@ import { } from "@renderer/features"; import type { GameShop, TorrentProgress } from "@types"; import { useDate } from "./use-date"; -import { formatBytes } from "@renderer/utils"; -import { GameStatus, GameStatusHelper } from "@shared"; +import { GameStatus, GameStatusHelper, formatBytes } from "@shared"; export function useDownload() { const { updateLibrary } = useLibrary(); diff --git a/src/renderer/src/pages/downloads/downloads.tsx b/src/renderer/src/pages/downloads/downloads.tsx index d8a4aec2..b95083dc 100644 --- a/src/renderer/src/pages/downloads/downloads.tsx +++ b/src/renderer/src/pages/downloads/downloads.tsx @@ -10,8 +10,7 @@ import { useEffect, useMemo, useRef, useState } from "react"; import { BinaryNotFoundModal } from "../shared-modals/binary-not-found-modal"; import * as styles from "./downloads.css"; import { DeleteModal } from "./delete-modal"; -import { formatBytes } from "@renderer/utils"; -import { Downloader, GameStatus, GameStatusHelper } from "@shared"; +import { Downloader, GameStatus, GameStatusHelper, formatBytes } from "@shared"; export function Downloads() { const { library, updateLibrary } = useLibrary(); diff --git a/src/renderer/src/pages/game-details/hero/hero-panel.tsx b/src/renderer/src/pages/game-details/hero/hero-panel.tsx index f156e5d2..138f27ec 100644 --- a/src/renderer/src/pages/game-details/hero/hero-panel.tsx +++ b/src/renderer/src/pages/game-details/hero/hero-panel.tsx @@ -6,9 +6,8 @@ import { useDownload } from "@renderer/hooks"; import type { Game, ShopDetails } from "@types"; import { formatDownloadProgress } from "@renderer/helpers"; -import { formatBytes } from "@renderer/utils"; import { HeroPanelActions } from "./hero-panel-actions"; -import { Downloader, GameStatus, GameStatusHelper } from "@shared"; +import { Downloader, GameStatus, GameStatusHelper, formatBytes } from "@shared"; import { BinaryNotFoundModal } from "../../shared-modals/binary-not-found-modal"; import * as styles from "./hero-panel.css"; diff --git a/src/renderer/src/pages/game-details/select-folder-modal.tsx b/src/renderer/src/pages/game-details/select-folder-modal.tsx index 0e3ae000..97567c98 100644 --- a/src/renderer/src/pages/game-details/select-folder-modal.tsx +++ b/src/renderer/src/pages/game-details/select-folder-modal.tsx @@ -3,10 +3,10 @@ import { GameRepack, ShopDetails } from "@types"; import { useEffect, useState } from "react"; import { Trans, useTranslation } from "react-i18next"; -import { formatBytes } from "@renderer/utils"; import { DiskSpace } from "check-disk-space"; import * as styles from "./select-folder-modal.css"; import { DownloadIcon } from "@primer/octicons-react"; +import { formatBytes } from "@shared"; export interface SelectFolderModalProps { visible: boolean; @@ -74,7 +74,7 @@ export function SelectFolderModal({ return ( - Save changes + {t("save_changes")} ); diff --git a/src/renderer/src/utils/format-bytes.ts b/src/renderer/src/utils/format-bytes.ts deleted file mode 100644 index b052b43b..00000000 --- a/src/renderer/src/utils/format-bytes.ts +++ /dev/null @@ -1,15 +0,0 @@ -const FORMAT = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; - -export const formatBytes = (bytes: number): string => { - if (!Number.isFinite(bytes) || isNaN(bytes) || bytes <= 0) { - return `0 ${FORMAT[0]}`; - } - - const byteKBase = 1024; - - const base = Math.floor(Math.log(bytes) / Math.log(byteKBase)); - - const formatedByte = bytes / byteKBase ** base; - - return `${Math.trunc(formatedByte * 10) / 10} ${FORMAT[base]}`; -}; diff --git a/src/renderer/src/utils/index.ts b/src/renderer/src/utils/index.ts deleted file mode 100644 index 7a828a7f..00000000 --- a/src/renderer/src/utils/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./format-bytes"; diff --git a/src/shared/index.ts b/src/shared/index.ts index bedf98fb..71021c13 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -14,6 +14,22 @@ export enum Downloader { Torrent, } +const FORMAT = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + +export const formatBytes = (bytes: number): string => { + if (!Number.isFinite(bytes) || isNaN(bytes) || bytes <= 0) { + return `0 ${FORMAT[0]}`; + } + + const byteKBase = 1024; + + const base = Math.floor(Math.log(bytes) / Math.log(byteKBase)); + + const formatedByte = bytes / byteKBase ** base; + + return `${Math.trunc(formatedByte * 10) / 10} ${FORMAT[base]}`; +}; + export class GameStatusHelper { public static isDownloading(status: GameStatus | null) { return (