mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-02-09 03:37:45 +03:00
feat: fixing real debrid download
This commit is contained in:
parent
fffea84ef7
commit
68b361e605
@ -34,8 +34,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-toolkit/preload": "^3.0.0",
|
"@electron-toolkit/preload": "^3.0.0",
|
||||||
"@electron-toolkit/utils": "^3.0.0",
|
"@electron-toolkit/utils": "^3.0.0",
|
||||||
"@fontsource/fira-mono": "^5.0.13",
|
"@fontsource/noto-sans": "^5.0.22",
|
||||||
"@fontsource/fira-sans": "^5.0.20",
|
|
||||||
"@primer/octicons-react": "^19.9.0",
|
"@primer/octicons-react": "^19.9.0",
|
||||||
"@reduxjs/toolkit": "^2.2.3",
|
"@reduxjs/toolkit": "^2.2.3",
|
||||||
"@sentry/electron": "^5.1.0",
|
"@sentry/electron": "^5.1.0",
|
||||||
|
@ -44,8 +44,6 @@ const startGameDownload = async (
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (game) {
|
if (game) {
|
||||||
console.log("game", game);
|
|
||||||
|
|
||||||
await gameRepository.update(
|
await gameRepository.update(
|
||||||
{
|
{
|
||||||
id: game.id,
|
id: game.id,
|
||||||
@ -97,8 +95,6 @@ const startGameDownload = async (
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(updatedGame);
|
|
||||||
|
|
||||||
createGame(updatedGame!);
|
createGame(updatedGame!);
|
||||||
|
|
||||||
await downloadQueueRepository.delete({ game: { id: updatedGame!.id } });
|
await downloadQueueRepository.delete({ game: { id: updatedGame!.id } });
|
||||||
|
@ -102,8 +102,6 @@ export class DownloadManager {
|
|||||||
const token = await GofileApi.authorize();
|
const token = await GofileApi.authorize();
|
||||||
const downloadLink = await GofileApi.getDownloadLink(id!);
|
const downloadLink = await GofileApi.getDownloadLink(id!);
|
||||||
|
|
||||||
console.log(downloadLink, token, "<<<");
|
|
||||||
|
|
||||||
GenericHTTPDownloader.startDownload(game, downloadLink, {
|
GenericHTTPDownloader.startDownload(game, downloadLink, {
|
||||||
Cookie: `accountToken=${token}`,
|
Cookie: `accountToken=${token}`,
|
||||||
});
|
});
|
||||||
|
@ -2,7 +2,7 @@ import { Game } from "@main/entity";
|
|||||||
import { gameRepository } from "@main/repository";
|
import { gameRepository } from "@main/repository";
|
||||||
import { calculateETA } from "./helpers";
|
import { calculateETA } from "./helpers";
|
||||||
import { DownloadProgress } from "@types";
|
import { DownloadProgress } from "@types";
|
||||||
import { HTTPDownload } from "./http-download";
|
import { HttpDownload } from "./http-download";
|
||||||
|
|
||||||
export class GenericHTTPDownloader {
|
export class GenericHTTPDownloader {
|
||||||
private static downloads = new Map<number, string>();
|
private static downloads = new Map<number, string>();
|
||||||
@ -11,7 +11,7 @@ export class GenericHTTPDownloader {
|
|||||||
public static async getStatus() {
|
public static async getStatus() {
|
||||||
if (this.downloadingGame) {
|
if (this.downloadingGame) {
|
||||||
const gid = this.downloads.get(this.downloadingGame.id)!;
|
const gid = this.downloads.get(this.downloadingGame.id)!;
|
||||||
const status = await HTTPDownload.getStatus(gid);
|
const status = HttpDownload.getStatus(gid);
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
const progress =
|
const progress =
|
||||||
@ -60,7 +60,7 @@ export class GenericHTTPDownloader {
|
|||||||
const gid = this.downloads.get(this.downloadingGame!.id!);
|
const gid = this.downloads.get(this.downloadingGame!.id!);
|
||||||
|
|
||||||
if (gid) {
|
if (gid) {
|
||||||
await HTTPDownload.pauseDownload(gid);
|
await HttpDownload.pauseDownload(gid);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.downloadingGame = null;
|
this.downloadingGame = null;
|
||||||
@ -76,12 +76,10 @@ export class GenericHTTPDownloader {
|
|||||||
|
|
||||||
if (this.downloads.has(game.id)) {
|
if (this.downloads.has(game.id)) {
|
||||||
await this.resumeDownload(game.id!);
|
await this.resumeDownload(game.id!);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (downloadUrl) {
|
const gid = await HttpDownload.startDownload(
|
||||||
const gid = await HTTPDownload.startDownload(
|
|
||||||
game.downloadPath!,
|
game.downloadPath!,
|
||||||
downloadUrl,
|
downloadUrl,
|
||||||
headers
|
headers
|
||||||
@ -89,13 +87,12 @@ export class GenericHTTPDownloader {
|
|||||||
|
|
||||||
this.downloads.set(game.id!, gid);
|
this.downloads.set(game.id!, gid);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static async cancelDownload(gameId: number) {
|
static async cancelDownload(gameId: number) {
|
||||||
const gid = this.downloads.get(gameId);
|
const gid = this.downloads.get(gameId);
|
||||||
|
|
||||||
if (gid) {
|
if (gid) {
|
||||||
await HTTPDownload.cancelDownload(gid);
|
await HttpDownload.cancelDownload(gid);
|
||||||
this.downloads.delete(gameId);
|
this.downloads.delete(gameId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,7 +101,7 @@ export class GenericHTTPDownloader {
|
|||||||
const gid = this.downloads.get(gameId);
|
const gid = this.downloads.get(gameId);
|
||||||
|
|
||||||
if (gid) {
|
if (gid) {
|
||||||
await HTTPDownload.resumeDownload(gid);
|
await HttpDownload.resumeDownload(gid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import { DownloadItem } from "electron";
|
|||||||
import { WindowManager } from "../window-manager";
|
import { WindowManager } from "../window-manager";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|
||||||
export class HTTPDownload {
|
export class HttpDownload {
|
||||||
private static id = 0;
|
private static id = 0;
|
||||||
|
|
||||||
private static downloads: Record<string, DownloadItem> = {};
|
private static downloads: Record<string, DownloadItem> = {};
|
||||||
|
@ -3,7 +3,7 @@ import { RealDebridClient } from "../real-debrid";
|
|||||||
import { gameRepository } from "@main/repository";
|
import { gameRepository } from "@main/repository";
|
||||||
import { calculateETA } from "./helpers";
|
import { calculateETA } from "./helpers";
|
||||||
import { DownloadProgress } from "@types";
|
import { DownloadProgress } from "@types";
|
||||||
import { HTTPDownload } from "./http-download";
|
import { HttpDownload } from "./http-download";
|
||||||
|
|
||||||
export class RealDebridDownloader {
|
export class RealDebridDownloader {
|
||||||
private static downloads = new Map<number, string>();
|
private static downloads = new Map<number, string>();
|
||||||
@ -13,19 +13,23 @@ export class RealDebridDownloader {
|
|||||||
|
|
||||||
private static async getRealDebridDownloadUrl() {
|
private static async getRealDebridDownloadUrl() {
|
||||||
if (this.realDebridTorrentId) {
|
if (this.realDebridTorrentId) {
|
||||||
const torrentInfo = await RealDebridClient.getTorrentInfo(
|
let torrentInfo = await RealDebridClient.getTorrentInfo(
|
||||||
this.realDebridTorrentId
|
this.realDebridTorrentId
|
||||||
);
|
);
|
||||||
|
|
||||||
const { status, links } = torrentInfo;
|
if (torrentInfo.status === "waiting_files_selection") {
|
||||||
|
|
||||||
if (status === "waiting_files_selection") {
|
|
||||||
await RealDebridClient.selectAllFiles(this.realDebridTorrentId);
|
await RealDebridClient.selectAllFiles(this.realDebridTorrentId);
|
||||||
return null;
|
|
||||||
|
torrentInfo = await RealDebridClient.getTorrentInfo(
|
||||||
|
this.realDebridTorrentId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { links, status } = torrentInfo;
|
||||||
|
|
||||||
if (status === "downloaded") {
|
if (status === "downloaded") {
|
||||||
const [link] = links;
|
const [link] = links;
|
||||||
|
|
||||||
const { download } = await RealDebridClient.unrestrictLink(link);
|
const { download } = await RealDebridClient.unrestrictLink(link);
|
||||||
return decodeURIComponent(download);
|
return decodeURIComponent(download);
|
||||||
}
|
}
|
||||||
@ -38,8 +42,6 @@ export class RealDebridDownloader {
|
|||||||
this.downloadingGame?.uri
|
this.downloadingGame?.uri
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log("download>>", download);
|
|
||||||
|
|
||||||
return decodeURIComponent(download);
|
return decodeURIComponent(download);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +51,7 @@ export class RealDebridDownloader {
|
|||||||
public static async getStatus() {
|
public static async getStatus() {
|
||||||
if (this.downloadingGame) {
|
if (this.downloadingGame) {
|
||||||
const gid = this.downloads.get(this.downloadingGame.id)!;
|
const gid = this.downloads.get(this.downloadingGame.id)!;
|
||||||
const status = await HTTPDownload.getStatus(gid);
|
const status = HttpDownload.getStatus(gid);
|
||||||
|
|
||||||
if (status) {
|
if (status) {
|
||||||
const progress =
|
const progress =
|
||||||
@ -91,33 +93,6 @@ 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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +100,7 @@ export class RealDebridDownloader {
|
|||||||
if (this.downloadingGame) {
|
if (this.downloadingGame) {
|
||||||
const gid = this.downloads.get(this.downloadingGame.id);
|
const gid = this.downloads.get(this.downloadingGame.id);
|
||||||
if (gid) {
|
if (gid) {
|
||||||
await HTTPDownload.pauseDownload(gid);
|
await HttpDownload.pauseDownload(gid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,18 +121,19 @@ export class RealDebridDownloader {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.downloadingGame = game;
|
||||||
|
|
||||||
const downloadUrl = await this.getRealDebridDownloadUrl();
|
const downloadUrl = await this.getRealDebridDownloadUrl();
|
||||||
|
|
||||||
if (downloadUrl) {
|
if (downloadUrl) {
|
||||||
this.realDebridTorrentId = null;
|
this.realDebridTorrentId = null;
|
||||||
|
|
||||||
const gid = await HTTPDownload.startDownload(
|
const gid = await HttpDownload.startDownload(
|
||||||
game.downloadPath!,
|
game.downloadPath!,
|
||||||
downloadUrl
|
downloadUrl
|
||||||
);
|
);
|
||||||
|
|
||||||
this.downloads.set(game.id!, gid);
|
this.downloads.set(game.id!, gid);
|
||||||
this.downloadingGame = game;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,7 +141,7 @@ export class RealDebridDownloader {
|
|||||||
const gid = this.downloads.get(gameId);
|
const gid = this.downloads.get(gameId);
|
||||||
|
|
||||||
if (gid) {
|
if (gid) {
|
||||||
await HTTPDownload.cancelDownload(gid);
|
await HttpDownload.cancelDownload(gid);
|
||||||
this.downloads.delete(gameId);
|
this.downloads.delete(gameId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +153,7 @@ export class RealDebridDownloader {
|
|||||||
const gid = this.downloads.get(gameId);
|
const gid = this.downloads.get(gameId);
|
||||||
|
|
||||||
if (gid) {
|
if (gid) {
|
||||||
await HTTPDownload.resumeDownload(gid);
|
await HttpDownload.resumeDownload(gid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ export class RealDebridClient {
|
|||||||
static async selectAllFiles(id: string) {
|
static async selectAllFiles(id: string) {
|
||||||
const searchParams = new URLSearchParams({ files: "all" });
|
const searchParams = new URLSearchParams({ files: "all" });
|
||||||
|
|
||||||
await this.instance.post(
|
return this.instance.post(
|
||||||
`/torrents/selectFiles/${id}`,
|
`/torrents/selectFiles/${id}`,
|
||||||
searchParams.toString()
|
searchParams.toString()
|
||||||
);
|
);
|
||||||
|
@ -26,7 +26,7 @@ globalStyle("html, body, #root, main", {
|
|||||||
globalStyle("body", {
|
globalStyle("body", {
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
userSelect: "none",
|
userSelect: "none",
|
||||||
fontFamily: "'Fira Mono', monospace",
|
fontFamily: "Noto Sans, sans-serif",
|
||||||
fontSize: vars.size.body,
|
fontSize: vars.size.body,
|
||||||
background: vars.color.background,
|
background: vars.color.background,
|
||||||
color: vars.color.body,
|
color: vars.color.body,
|
||||||
|
@ -45,7 +45,6 @@ export const description = style({
|
|||||||
maxWidth: "700px",
|
maxWidth: "700px",
|
||||||
color: vars.color.muted,
|
color: vars.color.muted,
|
||||||
textAlign: "left",
|
textAlign: "left",
|
||||||
fontFamily: "'Fira Sans', sans-serif",
|
|
||||||
lineHeight: "20px",
|
lineHeight: "20px",
|
||||||
marginTop: `${SPACING_UNIT * 2}px`,
|
marginTop: `${SPACING_UNIT * 2}px`,
|
||||||
});
|
});
|
||||||
|
@ -24,6 +24,7 @@ export const modal = recipe({
|
|||||||
animation: `${scaleFadeIn} 0.2s cubic-bezier(0.33, 1, 0.68, 1) 0s 1 normal none running`,
|
animation: `${scaleFadeIn} 0.2s cubic-bezier(0.33, 1, 0.68, 1) 0s 1 normal none running`,
|
||||||
backgroundColor: vars.color.background,
|
backgroundColor: vars.color.background,
|
||||||
borderRadius: "4px",
|
borderRadius: "4px",
|
||||||
|
minWidth: "400px",
|
||||||
maxWidth: "600px",
|
maxWidth: "600px",
|
||||||
color: vars.color.body,
|
color: vars.color.body,
|
||||||
maxHeight: "100%",
|
maxHeight: "100%",
|
||||||
|
@ -8,12 +8,10 @@ import { HashRouter, Route, Routes } from "react-router-dom";
|
|||||||
|
|
||||||
import * as Sentry from "@sentry/electron/renderer";
|
import * as Sentry from "@sentry/electron/renderer";
|
||||||
|
|
||||||
import "@fontsource/fira-mono/400.css";
|
import "@fontsource/noto-sans/400.css";
|
||||||
import "@fontsource/fira-mono/500.css";
|
import "@fontsource/noto-sans/500.css";
|
||||||
import "@fontsource/fira-mono/700.css";
|
import "@fontsource/noto-sans/700.css";
|
||||||
import "@fontsource/fira-sans/400.css";
|
|
||||||
import "@fontsource/fira-sans/500.css";
|
|
||||||
import "@fontsource/fira-sans/700.css";
|
|
||||||
import "react-loading-skeleton/dist/skeleton.css";
|
import "react-loading-skeleton/dist/skeleton.css";
|
||||||
|
|
||||||
import { App } from "./app";
|
import { App } from "./app";
|
||||||
|
@ -132,9 +132,7 @@ export function Downloads() {
|
|||||||
<ArrowDownIcon size={24} />
|
<ArrowDownIcon size={24} />
|
||||||
</div>
|
</div>
|
||||||
<h2>{t("no_downloads_title")}</h2>
|
<h2>{t("no_downloads_title")}</h2>
|
||||||
<p style={{ fontFamily: "Fira Sans" }}>
|
<p>{t("no_downloads_description")}</p>
|
||||||
{t("no_downloads_description")}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -19,7 +19,10 @@ export function DescriptionHeader() {
|
|||||||
date: shopDetails?.release_date.date,
|
date: shopDetails?.release_date.date,
|
||||||
})}
|
})}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
{Array.isArray(shopDetails.publishers) && (
|
||||||
<p>{t("publisher", { publisher: shopDetails.publishers[0] })}</p>
|
<p>{t("publisher", { publisher: shopDetails.publishers[0] })}</p>
|
||||||
|
)}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -101,7 +101,6 @@ export const descriptionContent = style({
|
|||||||
export const description = style({
|
export const description = style({
|
||||||
userSelect: "text",
|
userSelect: "text",
|
||||||
lineHeight: "22px",
|
lineHeight: "22px",
|
||||||
fontFamily: "'Fira Sans', sans-serif",
|
|
||||||
fontSize: "16px",
|
fontSize: "16px",
|
||||||
padding: `${SPACING_UNIT * 3}px ${SPACING_UNIT * 2}px`,
|
padding: `${SPACING_UNIT * 3}px ${SPACING_UNIT * 2}px`,
|
||||||
"@media": {
|
"@media": {
|
||||||
|
@ -9,6 +9,7 @@ export const panel = recipe({
|
|||||||
height: "72px",
|
height: "72px",
|
||||||
minHeight: "72px",
|
minHeight: "72px",
|
||||||
padding: `${SPACING_UNIT * 2}px ${SPACING_UNIT * 3}px`,
|
padding: `${SPACING_UNIT * 2}px ${SPACING_UNIT * 3}px`,
|
||||||
|
backgroundColor: vars.color.darkBackground,
|
||||||
display: "flex",
|
display: "flex",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
|
@ -60,14 +60,22 @@ export function DownloadSettingsModal({
|
|||||||
.then((defaultDownloadsPath) => setSelectedPath(defaultDownloadsPath));
|
.then((defaultDownloadsPath) => setSelectedPath(defaultDownloadsPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
const filteredDownloaders = downloaders.filter((downloader) => {
|
||||||
userPreferences?.realDebridApiToken &&
|
if (downloader === Downloader.RealDebrid)
|
||||||
downloaders.includes(Downloader.RealDebrid)
|
return userPreferences?.realDebridApiToken;
|
||||||
) {
|
return true;
|
||||||
setSelectedDownloader(Downloader.RealDebrid);
|
});
|
||||||
} else {
|
|
||||||
setSelectedDownloader(downloaders[0]);
|
/* Gives preference to Real Debrid */
|
||||||
}
|
const selectedDownloader = filteredDownloaders.includes(
|
||||||
|
Downloader.RealDebrid
|
||||||
|
)
|
||||||
|
? Downloader.RealDebrid
|
||||||
|
: filteredDownloaders[0];
|
||||||
|
|
||||||
|
setSelectedDownloader(
|
||||||
|
selectedDownloader === undefined ? null : selectedDownloader
|
||||||
|
);
|
||||||
}, [
|
}, [
|
||||||
userPreferences?.downloadsPath,
|
userPreferences?.downloadsPath,
|
||||||
downloaders,
|
downloaders,
|
||||||
|
@ -15,7 +15,6 @@ export const gameOptionHeader = style({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const gameOptionHeaderDescription = style({
|
export const gameOptionHeaderDescription = style({
|
||||||
fontFamily: "'Fira Sans', sans-serif",
|
|
||||||
fontWeight: "400",
|
fontWeight: "400",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -46,7 +46,6 @@ export const requirementButton = style({
|
|||||||
export const requirementsDetails = style({
|
export const requirementsDetails = style({
|
||||||
padding: `${SPACING_UNIT * 2}px`,
|
padding: `${SPACING_UNIT * 2}px`,
|
||||||
lineHeight: "22px",
|
lineHeight: "22px",
|
||||||
fontFamily: "'Fira Sans', sans-serif",
|
|
||||||
fontSize: "16px",
|
fontSize: "16px",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -82,9 +82,7 @@ export function SettingsDownloadSources() {
|
|||||||
onAddDownloadSource={handleAddDownloadSource}
|
onAddDownloadSource={handleAddDownloadSource}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<p style={{ fontFamily: '"Fira Sans"' }}>
|
<p>{t("download_sources_description")}</p>
|
||||||
{t("download_sources_description")}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className={styles.downloadSourcesHeader}>
|
<div className={styles.downloadSourcesHeader}>
|
||||||
<Button
|
<Button
|
||||||
|
@ -9,6 +9,5 @@ export const form = style({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const description = style({
|
export const description = style({
|
||||||
fontFamily: "'Fira Sans', sans-serif",
|
|
||||||
marginBottom: `${SPACING_UNIT * 2}px`,
|
marginBottom: `${SPACING_UNIT * 2}px`,
|
||||||
});
|
});
|
||||||
|
@ -25,9 +25,7 @@ export const UserBlockModal = ({
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
>
|
>
|
||||||
<div className={styles.signOutModalContent}>
|
<div className={styles.signOutModalContent}>
|
||||||
<p style={{ fontFamily: "Fira Sans" }}>
|
<p>{t("user_block_modal_text", { displayName })}</p>
|
||||||
{t("user_block_modal_text", { displayName })}
|
|
||||||
</p>
|
|
||||||
<div className={styles.signOutModalButtonsContainer}>
|
<div className={styles.signOutModalButtonsContainer}>
|
||||||
<Button onClick={onConfirm} theme="danger">
|
<Button onClick={onConfirm} theme="danger">
|
||||||
{t("block_user")}
|
{t("block_user")}
|
||||||
|
@ -371,11 +371,7 @@ export function UserContent({
|
|||||||
<TelescopeIcon size={24} />
|
<TelescopeIcon size={24} />
|
||||||
</div>
|
</div>
|
||||||
<h2>{t("no_recent_activity_title")}</h2>
|
<h2>{t("no_recent_activity_title")}</h2>
|
||||||
{isMe && (
|
{isMe && <p>{t("no_recent_activity_description")}</p>}
|
||||||
<p style={{ fontFamily: "Fira Sans" }}>
|
|
||||||
{t("no_recent_activity_description")}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
|
@ -23,7 +23,7 @@ export const UserSignOutModal = ({
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
>
|
>
|
||||||
<div className={styles.signOutModalContent}>
|
<div className={styles.signOutModalContent}>
|
||||||
<p style={{ fontFamily: "Fira Sans" }}>{t("sign_out_modal_text")}</p>
|
<p>{t("sign_out_modal_text")}</p>
|
||||||
<div className={styles.signOutModalButtonsContainer}>
|
<div className={styles.signOutModalButtonsContainer}>
|
||||||
<Button onClick={onConfirm} theme="danger">
|
<Button onClick={onConfirm} theme="danger">
|
||||||
{t("sign_out")}
|
{t("sign_out")}
|
||||||
|
@ -53,6 +53,8 @@ export const removeSpecialEditionFromName = (name: string) =>
|
|||||||
export const removeDuplicateSpaces = (name: string) =>
|
export const removeDuplicateSpaces = (name: string) =>
|
||||||
name.replace(/\s{2,}/g, " ");
|
name.replace(/\s{2,}/g, " ");
|
||||||
|
|
||||||
|
export const replaceDotsWithSpace = (name: string) => name.replace(/\./g, " ");
|
||||||
|
|
||||||
export const replaceUnderscoreWithSpace = (name: string) =>
|
export const replaceUnderscoreWithSpace = (name: string) =>
|
||||||
name.replace(/_/g, " ");
|
name.replace(/_/g, " ");
|
||||||
|
|
||||||
@ -60,6 +62,7 @@ export const formatName = pipe<string>(
|
|||||||
removeReleaseYearFromName,
|
removeReleaseYearFromName,
|
||||||
removeSpecialEditionFromName,
|
removeSpecialEditionFromName,
|
||||||
replaceUnderscoreWithSpace,
|
replaceUnderscoreWithSpace,
|
||||||
|
replaceDotsWithSpace,
|
||||||
(str) => str.replace(/DIRECTOR'S CUT/g, ""),
|
(str) => str.replace(/DIRECTOR'S CUT/g, ""),
|
||||||
removeSymbolsFromName,
|
removeSymbolsFromName,
|
||||||
removeDuplicateSpaces,
|
removeDuplicateSpaces,
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
import libtorrent as lt
|
|
||||||
|
|
||||||
class Downloader:
|
|
||||||
def __init__(self, port: str):
|
|
||||||
self.torrent_handles = {}
|
|
||||||
self.downloading_game_id = -1
|
|
||||||
self.session = lt.session({'listen_interfaces': '0.0.0.0:{port}'.format(port=port)})
|
|
||||||
|
|
||||||
def start_download(self, game_id: int, magnet: str, save_path: str):
|
|
||||||
params = {'url': magnet, 'save_path': save_path}
|
|
||||||
torrent_handle = self.session.add_torrent(params)
|
|
||||||
self.torrent_handles[game_id] = torrent_handle
|
|
||||||
torrent_handle.set_flags(lt.torrent_flags.auto_managed)
|
|
||||||
torrent_handle.resume()
|
|
||||||
|
|
||||||
self.downloading_game_id = game_id
|
|
||||||
|
|
||||||
def pause_download(self, game_id: int):
|
|
||||||
torrent_handle = self.torrent_handles.get(game_id)
|
|
||||||
if torrent_handle:
|
|
||||||
torrent_handle.pause()
|
|
||||||
torrent_handle.unset_flags(lt.torrent_flags.auto_managed)
|
|
||||||
self.downloading_game_id = -1
|
|
||||||
|
|
||||||
def cancel_download(self, game_id: int):
|
|
||||||
torrent_handle = self.torrent_handles.get(game_id)
|
|
||||||
if torrent_handle:
|
|
||||||
torrent_handle.pause()
|
|
||||||
self.session.remove_torrent(torrent_handle)
|
|
||||||
self.torrent_handles[game_id] = None
|
|
||||||
self.downloading_game_id = -1
|
|
||||||
|
|
||||||
def abort_session(self):
|
|
||||||
for game_id in self.torrent_handles:
|
|
||||||
torrent_handle = self.torrent_handles[game_id]
|
|
||||||
torrent_handle.pause()
|
|
||||||
self.session.remove_torrent(torrent_handle)
|
|
||||||
|
|
||||||
self.session.abort()
|
|
||||||
self.torrent_handles = {}
|
|
||||||
self.downloading_game_id = -1
|
|
||||||
|
|
||||||
def get_download_status(self):
|
|
||||||
if self.downloading_game_id == -1:
|
|
||||||
return None
|
|
||||||
|
|
||||||
torrent_handle = self.torrent_handles.get(self.downloading_game_id)
|
|
||||||
|
|
||||||
status = torrent_handle.status()
|
|
||||||
info = torrent_handle.get_torrent_info()
|
|
||||||
|
|
||||||
return {
|
|
||||||
'folderName': info.name() if info else "",
|
|
||||||
'fileSize': info.total_size() if info else 0,
|
|
||||||
'gameId': self.downloading_game_id,
|
|
||||||
'progress': status.progress,
|
|
||||||
'downloadSpeed': status.download_rate,
|
|
||||||
'numPeers': status.num_peers,
|
|
||||||
'numSeeds': status.num_seeds,
|
|
||||||
'status': status.state,
|
|
||||||
'bytesDownloaded': status.progress * info.total_size() if info else status.all_time_download,
|
|
||||||
}
|
|
@ -3,19 +3,19 @@ from http.server import HTTPServer, BaseHTTPRequestHandler
|
|||||||
import json
|
import json
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
import psutil
|
import psutil
|
||||||
from downloader import Downloader
|
from torrent_downloader import TorrentDownloader
|
||||||
|
|
||||||
torrent_port = sys.argv[1]
|
torrent_port = sys.argv[1]
|
||||||
http_port = sys.argv[2]
|
http_port = sys.argv[2]
|
||||||
rpc_password = sys.argv[3]
|
rpc_password = sys.argv[3]
|
||||||
start_download_payload = sys.argv[4]
|
start_download_payload = sys.argv[4]
|
||||||
|
|
||||||
downloader = None
|
torrent_downloader = None
|
||||||
|
|
||||||
if start_download_payload:
|
if start_download_payload:
|
||||||
initial_download = json.loads(urllib.parse.unquote(start_download_payload))
|
initial_download = json.loads(urllib.parse.unquote(start_download_payload))
|
||||||
downloader = Downloader(torrent_port)
|
torrent_downloader = TorrentDownloader(torrent_port)
|
||||||
downloader.start_download(initial_download['game_id'], initial_download['magnet'], initial_download['save_path'])
|
torrent_downloader.start_download(initial_download['game_id'], initial_download['magnet'], initial_download['save_path'])
|
||||||
|
|
||||||
class Handler(BaseHTTPRequestHandler):
|
class Handler(BaseHTTPRequestHandler):
|
||||||
rpc_password_header = 'x-hydra-rpc-password'
|
rpc_password_header = 'x-hydra-rpc-password'
|
||||||
@ -31,7 +31,7 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
self.send_header("Content-type", "application/json")
|
self.send_header("Content-type", "application/json")
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
|
|
||||||
status = downloader.get_download_status()
|
status = torrent_downloader.get_download_status()
|
||||||
|
|
||||||
self.wfile.write(json.dumps(status).encode('utf-8'))
|
self.wfile.write(json.dumps(status).encode('utf-8'))
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
self.wfile.write(json.dumps(process_list).encode('utf-8'))
|
self.wfile.write(json.dumps(process_list).encode('utf-8'))
|
||||||
|
|
||||||
def do_POST(self):
|
def do_POST(self):
|
||||||
global downloader
|
global torrent_downloader
|
||||||
|
|
||||||
if self.path == "/action":
|
if self.path == "/action":
|
||||||
if self.headers.get(self.rpc_password_header) != rpc_password:
|
if self.headers.get(self.rpc_password_header) != rpc_password:
|
||||||
@ -66,18 +66,18 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
post_data = self.rfile.read(content_length)
|
post_data = self.rfile.read(content_length)
|
||||||
data = json.loads(post_data.decode('utf-8'))
|
data = json.loads(post_data.decode('utf-8'))
|
||||||
|
|
||||||
if downloader is None:
|
if torrent_downloader is None:
|
||||||
downloader = Downloader(torrent_port)
|
torrent_downloader = TorrentDownloader(torrent_port)
|
||||||
|
|
||||||
if data['action'] == 'start':
|
if data['action'] == 'start':
|
||||||
downloader.start_download(data['game_id'], data['magnet'], data['save_path'])
|
torrent_downloader.start_download(data['game_id'], data['magnet'], data['save_path'])
|
||||||
elif data['action'] == 'pause':
|
elif data['action'] == 'pause':
|
||||||
downloader.pause_download(data['game_id'])
|
torrent_downloader.pause_download(data['game_id'])
|
||||||
elif data['action'] == 'cancel':
|
elif data['action'] == 'cancel':
|
||||||
downloader.cancel_download(data['game_id'])
|
torrent_downloader.cancel_download(data['game_id'])
|
||||||
elif data['action'] == 'kill-torrent':
|
elif data['action'] == 'kill-torrent':
|
||||||
downloader.abort_session()
|
torrent_downloader.abort_session()
|
||||||
downloader = None
|
torrent_downloader = None
|
||||||
|
|
||||||
self.send_response(200)
|
self.send_response(200)
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
|
158
torrent-client/torrent_downloader.py
Normal file
158
torrent-client/torrent_downloader.py
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
import libtorrent as lt
|
||||||
|
|
||||||
|
class TorrentDownloader:
|
||||||
|
def __init__(self, port: str):
|
||||||
|
self.torrent_handles = {}
|
||||||
|
self.downloading_game_id = -1
|
||||||
|
self.session = lt.session({'listen_interfaces': '0.0.0.0:{port}'.format(port=port)})
|
||||||
|
self.trackers = [
|
||||||
|
"udp://tracker.opentrackr.org:1337/announce",
|
||||||
|
"http://tracker.opentrackr.org:1337/announce",
|
||||||
|
"udp://open.tracker.cl:1337/announce",
|
||||||
|
"udp://open.demonii.com:1337/announce",
|
||||||
|
"udp://open.stealth.si:80/announce",
|
||||||
|
"udp://tracker.torrent.eu.org:451/announce",
|
||||||
|
"udp://exodus.desync.com:6969/announce",
|
||||||
|
"udp://tracker.theoks.net:6969/announce",
|
||||||
|
"udp://tracker-udp.gbitt.info:80/announce",
|
||||||
|
"udp://explodie.org:6969/announce",
|
||||||
|
"https://tracker.tamersunion.org:443/announce",
|
||||||
|
"udp://tracker2.dler.org:80/announce",
|
||||||
|
"udp://tracker1.myporn.club:9337/announce",
|
||||||
|
"udp://tracker.tiny-vps.com:6969/announce",
|
||||||
|
"udp://tracker.dler.org:6969/announce",
|
||||||
|
"udp://tracker.bittor.pw:1337/announce",
|
||||||
|
"udp://tracker.0x7c0.com:6969/announce",
|
||||||
|
"udp://retracker01-msk-virt.corbina.net:80/announce",
|
||||||
|
"udp://opentracker.io:6969/announce",
|
||||||
|
"udp://open.free-tracker.ga:6969/announce",
|
||||||
|
"udp://new-line.net:6969/announce",
|
||||||
|
"udp://moonburrow.club:6969/announce",
|
||||||
|
"udp://leet-tracker.moe:1337/announce",
|
||||||
|
"udp://bt2.archive.org:6969/announce",
|
||||||
|
"udp://bt1.archive.org:6969/announce",
|
||||||
|
"http://tracker2.dler.org:80/announce",
|
||||||
|
"http://tracker1.bt.moack.co.kr:80/announce",
|
||||||
|
"http://tracker.dler.org:6969/announce",
|
||||||
|
"http://tr.kxmp.cf:80/announce",
|
||||||
|
"udp://u.peer-exchange.download:6969/announce",
|
||||||
|
"udp://ttk2.nbaonlineservice.com:6969/announce",
|
||||||
|
"udp://tracker.tryhackx.org:6969/announce",
|
||||||
|
"udp://tracker.srv00.com:6969/announce",
|
||||||
|
"udp://tracker.skynetcloud.site:6969/announce",
|
||||||
|
"udp://tracker.jamesthebard.net:6969/announce",
|
||||||
|
"udp://tracker.fnix.net:6969/announce",
|
||||||
|
"udp://tracker.filemail.com:6969/announce",
|
||||||
|
"udp://tracker.farted.net:6969/announce",
|
||||||
|
"udp://tracker.edkj.club:6969/announce",
|
||||||
|
"udp://tracker.dump.cl:6969/announce",
|
||||||
|
"udp://tracker.deadorbit.nl:6969/announce",
|
||||||
|
"udp://tracker.darkness.services:6969/announce",
|
||||||
|
"udp://tracker.ccp.ovh:6969/announce",
|
||||||
|
"udp://tamas3.ynh.fr:6969/announce",
|
||||||
|
"udp://ryjer.com:6969/announce",
|
||||||
|
"udp://run.publictracker.xyz:6969/announce",
|
||||||
|
"udp://public.tracker.vraphim.com:6969/announce",
|
||||||
|
"udp://p4p.arenabg.com:1337/announce",
|
||||||
|
"udp://p2p.publictracker.xyz:6969/announce",
|
||||||
|
"udp://open.u-p.pw:6969/announce",
|
||||||
|
"udp://open.publictracker.xyz:6969/announce",
|
||||||
|
"udp://open.dstud.io:6969/announce",
|
||||||
|
"udp://open.demonoid.ch:6969/announce",
|
||||||
|
"udp://odd-hd.fr:6969/announce",
|
||||||
|
"udp://martin-gebhardt.eu:25/announce",
|
||||||
|
"udp://jutone.com:6969/announce",
|
||||||
|
"udp://isk.richardsw.club:6969/announce",
|
||||||
|
"udp://evan.im:6969/announce",
|
||||||
|
"udp://epider.me:6969/announce",
|
||||||
|
"udp://d40969.acod.regrucolo.ru:6969/announce",
|
||||||
|
"udp://bt.rer.lol:6969/announce",
|
||||||
|
"udp://amigacity.xyz:6969/announce",
|
||||||
|
"udp://1c.premierzal.ru:6969/announce",
|
||||||
|
"https://trackers.run:443/announce",
|
||||||
|
"https://tracker.yemekyedim.com:443/announce",
|
||||||
|
"https://tracker.renfei.net:443/announce",
|
||||||
|
"https://tracker.pmman.tech:443/announce",
|
||||||
|
"https://tracker.lilithraws.org:443/announce",
|
||||||
|
"https://tracker.imgoingto.icu:443/announce",
|
||||||
|
"https://tracker.cloudit.top:443/announce",
|
||||||
|
"https://tracker-zhuqiy.dgj055.icu:443/announce",
|
||||||
|
"http://tracker.renfei.net:8080/announce",
|
||||||
|
"http://tracker.mywaifu.best:6969/announce",
|
||||||
|
"http://tracker.ipv6tracker.org:80/announce",
|
||||||
|
"http://tracker.files.fm:6969/announce",
|
||||||
|
"http://tracker.edkj.club:6969/announce",
|
||||||
|
"http://tracker.bt4g.com:2095/announce",
|
||||||
|
"http://tracker-zhuqiy.dgj055.icu:80/announce",
|
||||||
|
"http://t1.aag.moe:17715/announce",
|
||||||
|
"http://t.overflow.biz:6969/announce",
|
||||||
|
"http://bittorrent-tracker.e-n-c-r-y-p-t.net:1337/announce",
|
||||||
|
"udp://torrents.artixlinux.org:6969/announce",
|
||||||
|
"udp://mail.artixlinux.org:6969/announce",
|
||||||
|
"udp://ipv4.rer.lol:2710/announce",
|
||||||
|
"udp://concen.org:6969/announce",
|
||||||
|
"udp://bt.rer.lol:2710/announce",
|
||||||
|
"udp://aegir.sexy:6969/announce",
|
||||||
|
"https://www.peckservers.com:9443/announce",
|
||||||
|
"https://tracker.ipfsscan.io:443/announce",
|
||||||
|
"https://tracker.gcrenwp.top:443/announce",
|
||||||
|
"http://www.peckservers.com:9000/announce",
|
||||||
|
"http://tracker1.itzmx.com:8080/announce",
|
||||||
|
"http://ch3oh.ru:6969/announce",
|
||||||
|
"http://bvarf.tracker.sh:2086/announce",
|
||||||
|
]
|
||||||
|
|
||||||
|
def start_download(self, game_id: int, magnet: str, save_path: str):
|
||||||
|
params = {'url': magnet, 'save_path': save_path, 'trackers': self.trackers}
|
||||||
|
torrent_handle = self.session.add_torrent(params)
|
||||||
|
self.torrent_handles[game_id] = torrent_handle
|
||||||
|
torrent_handle.set_flags(lt.torrent_flags.auto_managed)
|
||||||
|
torrent_handle.resume()
|
||||||
|
|
||||||
|
self.downloading_game_id = game_id
|
||||||
|
|
||||||
|
def pause_download(self, game_id: int):
|
||||||
|
torrent_handle = self.torrent_handles.get(game_id)
|
||||||
|
if torrent_handle:
|
||||||
|
torrent_handle.pause()
|
||||||
|
torrent_handle.unset_flags(lt.torrent_flags.auto_managed)
|
||||||
|
self.downloading_game_id = -1
|
||||||
|
|
||||||
|
def cancel_download(self, game_id: int):
|
||||||
|
torrent_handle = self.torrent_handles.get(game_id)
|
||||||
|
if torrent_handle:
|
||||||
|
torrent_handle.pause()
|
||||||
|
self.session.remove_torrent(torrent_handle)
|
||||||
|
self.torrent_handles[game_id] = None
|
||||||
|
self.downloading_game_id = -1
|
||||||
|
|
||||||
|
def abort_session(self):
|
||||||
|
for game_id in self.torrent_handles:
|
||||||
|
torrent_handle = self.torrent_handles[game_id]
|
||||||
|
torrent_handle.pause()
|
||||||
|
self.session.remove_torrent(torrent_handle)
|
||||||
|
|
||||||
|
self.session.abort()
|
||||||
|
self.torrent_handles = {}
|
||||||
|
self.downloading_game_id = -1
|
||||||
|
|
||||||
|
def get_download_status(self):
|
||||||
|
if self.downloading_game_id == -1:
|
||||||
|
return None
|
||||||
|
|
||||||
|
torrent_handle = self.torrent_handles.get(self.downloading_game_id)
|
||||||
|
|
||||||
|
status = torrent_handle.status()
|
||||||
|
info = torrent_handle.get_torrent_info()
|
||||||
|
|
||||||
|
return {
|
||||||
|
'folderName': info.name() if info else "",
|
||||||
|
'fileSize': info.total_size() if info else 0,
|
||||||
|
'gameId': self.downloading_game_id,
|
||||||
|
'progress': status.progress,
|
||||||
|
'downloadSpeed': status.download_rate,
|
||||||
|
'numPeers': status.num_peers,
|
||||||
|
'numSeeds': status.num_seeds,
|
||||||
|
'status': status.state,
|
||||||
|
'bytesDownloaded': status.progress * info.total_size() if info else status.all_time_download,
|
||||||
|
}
|
13
yarn.lock
13
yarn.lock
@ -943,15 +943,10 @@
|
|||||||
resolved "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz"
|
resolved "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz"
|
||||||
integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==
|
integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==
|
||||||
|
|
||||||
"@fontsource/fira-mono@^5.0.13":
|
"@fontsource/noto-sans@^5.0.22":
|
||||||
version "5.0.13"
|
version "5.0.22"
|
||||||
resolved "https://registry.npmjs.org/@fontsource/fira-mono/-/fira-mono-5.0.13.tgz"
|
resolved "https://registry.yarnpkg.com/@fontsource/noto-sans/-/noto-sans-5.0.22.tgz#2c5249347ba84fef16e71a58e0ec01b460174093"
|
||||||
integrity sha512-fZDjR2BdAqmauEbTjcIT62zYzbOgDa5+IQH34D2k8Pxmy1T815mAqQkZciWZVQ9dc/BgdTtTUV9HJ2ulBNwchg==
|
integrity sha512-PwjvKPGFbgpwfKjWZj1zeUvd7ExUW2AqHE9PF9ysAJ2gOuzIHWE6mEVIlchYif7WC2pQhn+g0w6xooCObVi+4A==
|
||||||
|
|
||||||
"@fontsource/fira-sans@^5.0.20":
|
|
||||||
version "5.0.20"
|
|
||||||
resolved "https://registry.npmjs.org/@fontsource/fira-sans/-/fira-sans-5.0.20.tgz"
|
|
||||||
integrity sha512-inmUjoKPrgnO4uUaZTAgP0b6YdhDfA52axHXvdTwgLvwd2kn3ZgK52UZoxD0VnrvTOjLA/iE4oC0tNtz4nyb5g==
|
|
||||||
|
|
||||||
"@humanwhocodes/config-array@^0.11.14":
|
"@humanwhocodes/config-array@^0.11.14":
|
||||||
version "0.11.14"
|
version "0.11.14"
|
||||||
|
Loading…
Reference in New Issue
Block a user