diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts index 0d9f5cbb7..134a74e60 100644 --- a/src/main/services/download/download-manager.ts +++ b/src/main/services/download/download-manager.ts @@ -8,7 +8,7 @@ import { } from "@main/repository"; import { publishDownloadCompleteNotification } from "../notifications"; import type { DownloadProgress } from "@types"; -import { GofileApi, QiwiApi } from "../hosters"; +import { GofileApi, QiwiApi, DatanodesApi } from "../hosters"; import { PythonRPC } from "../python-rpc"; import { LibtorrentPayload, @@ -277,6 +277,16 @@ export class DownloadManager { save_path: game.downloadPath!, }; } + case Downloader.Datanodes: { + const downloadUrl = await DatanodesApi.getDownloadUrl(game.uri!); + + return { + action: "start", + game_id: game.id, + url: downloadUrl, + save_path: game.downloadPath!, + }; + } case Downloader.Torrent: return { action: "start", diff --git a/src/main/services/hosters/datanodes.ts b/src/main/services/hosters/datanodes.ts new file mode 100644 index 000000000..28bd026e2 --- /dev/null +++ b/src/main/services/hosters/datanodes.ts @@ -0,0 +1,51 @@ +import axios, { AxiosResponse } from "axios"; + +export class DatanodesApi { + private static session = axios.create({}); + + public static async getDownloadUrl(downloadUrl: string): Promise { + const parsedUrl = new URL(downloadUrl); + const pathSegments = parsedUrl.pathname.split("/"); + + const fileCode = decodeURIComponent(pathSegments[1]); + const fileName = decodeURIComponent(pathSegments[pathSegments.length - 1]); + + const headers: Record = { + "Content-Type": "application/x-www-form-urlencoded", + Cookie: `lang=english; file_name=${fileName}; file_code=${fileCode};`, + Host: "datanodes.to", + Origin: "https://datanodes.to", + Referer: "https://datanodes.to/download", + "User-Agent": + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36", + }; + + const payload = new URLSearchParams({ + op: "download2", + id: fileCode, + rand: "", + referer: "https://datanodes.to/download", + method_free: "Free Download >>", + method_premium: "", + adblock_detected: "", + }); + + const config = { + headers, + maxRedirects: 0, + validateStatus: (status: number) => status === 302 || status < 400, + }; + + const response: AxiosResponse = await DatanodesApi.session.post( + "https://datanodes.to/download", + payload, + config + ); + + if (response.status === 302) { + return response.headers["location"]; + } + + return ""; + } +} diff --git a/src/main/services/hosters/index.ts b/src/main/services/hosters/index.ts index 4c5b18035..8cff7bd25 100644 --- a/src/main/services/hosters/index.ts +++ b/src/main/services/hosters/index.ts @@ -1,2 +1,3 @@ export * from "./gofile"; export * from "./qiwi"; +export * from "./datanodes"; diff --git a/src/renderer/src/constants.ts b/src/renderer/src/constants.ts index 745418379..d0797caf2 100644 --- a/src/renderer/src/constants.ts +++ b/src/renderer/src/constants.ts @@ -8,6 +8,7 @@ export const DOWNLOADER_NAME = { [Downloader.Gofile]: "Gofile", [Downloader.PixelDrain]: "PixelDrain", [Downloader.Qiwi]: "Qiwi", + [Downloader.Datanodes]: "Datanodes", }; export const MAX_MINUTES_TO_SHOW_IN_PLAYTIME = 120; diff --git a/src/shared/constants.ts b/src/shared/constants.ts index 2d313abb9..6b332d40a 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -4,6 +4,7 @@ export enum Downloader { Gofile, PixelDrain, Qiwi, + Datanodes, } export enum DownloadSourceStatus { diff --git a/src/shared/index.ts b/src/shared/index.ts index 858683919..7d612a170 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -87,6 +87,7 @@ export const getDownloadersForUri = (uri: string) => { if (uri.startsWith("https://pixeldrain.com")) return [Downloader.PixelDrain]; if (uri.startsWith("https://qiwi.gg")) return [Downloader.Qiwi]; + if (uri.startsWith("https://datanodes.to")) return [Downloader.Datanodes]; if (realDebridHosts.some((host) => uri.startsWith(host))) return [Downloader.RealDebrid];