diff --git a/package-lock.json b/package-lock.json
index 1ea49bd..7de7024 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "real-debrid-downloader",
- "version": "1.1.19",
+ "version": "1.1.20",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "real-debrid-downloader",
- "version": "1.1.19",
+ "version": "1.1.20",
"license": "MIT",
"dependencies": {
"adm-zip": "^0.5.16",
diff --git a/package.json b/package.json
index 7e08803..314bf43 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "real-debrid-downloader",
- "version": "1.1.19",
+ "version": "1.1.20",
"description": "Real-Debrid Downloader Desktop (Electron + React + TypeScript)",
"main": "build/main/main/main.js",
"author": "Sucukdeluxe",
diff --git a/scripts/mega_web_generate_download_test.mjs b/scripts/mega_web_generate_download_test.mjs
new file mode 100644
index 0000000..0066daa
--- /dev/null
+++ b/scripts/mega_web_generate_download_test.mjs
@@ -0,0 +1,139 @@
+const LOGIN = process.env.MEGA_LOGIN || "";
+const PASSWORD = process.env.MEGA_PASSWORD || "";
+
+const LINKS = [
+ "https://rapidgator.net/file/90b5397dfc3e1a0e561db7d6b89d5604/scnb-rrw7-S08E01.part1.rar.html",
+ "https://rapidgator.net/file/8ddf856dc833310c5cae9db82caf9682/scnb-rrw7-S08E01.part2.rar.html",
+ "https://rapidgator.net/file/440eed67d266476866332ae224c3fad5/scnb-rrw7-S08E01.part3.rar.html"
+];
+
+if (!LOGIN || !PASSWORD) {
+ throw new Error("Set MEGA_LOGIN and MEGA_PASSWORD env vars");
+}
+
+function sleep(ms) {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+}
+
+function cookieFrom(headers) {
+ const raw = headers.get("set-cookie") || "";
+ return raw.split(",").map((x) => x.split(";")[0].trim()).filter(Boolean).join("; ");
+}
+
+function parseDebridCodes(html) {
+ const re = /processDebrid\((\d+),'([^']+)',0\)/g;
+ const out = [];
+ let m;
+ while ((m = re.exec(html)) !== null) {
+ out.push({ id: Number(m[1]), code: m[2] });
+ }
+ return out;
+}
+
+async function resolveCode(cookie, code) {
+ for (let attempt = 1; attempt <= 50; attempt += 1) {
+ const res = await fetch("https://www.mega-debrid.eu/index.php?ajax=debrid&json", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded",
+ "User-Agent": "Mozilla/5.0",
+ Cookie: cookie,
+ Referer: "https://www.mega-debrid.eu/index.php?page=debrideur&lang=de"
+ },
+ body: new URLSearchParams({
+ code,
+ autodl: "0"
+ })
+ });
+ const text = (await res.text()).trim();
+ if (text === "reload") {
+ await sleep(800);
+ continue;
+ }
+ if (text === "false") {
+ return { ok: false, reason: "false" };
+ }
+ try {
+ const parsed = JSON.parse(text);
+ if (parsed?.link) {
+ return { ok: true, link: String(parsed.link), text: String(parsed.text || "") };
+ }
+ return { ok: false, reason: text };
+ } catch {
+ return { ok: false, reason: text };
+ }
+ }
+ return { ok: false, reason: "timeout" };
+}
+
+async function probeDownload(url) {
+ const res = await fetch(url, {
+ method: "GET",
+ headers: {
+ Range: "bytes=0-4095",
+ "User-Agent": "Mozilla/5.0"
+ },
+ redirect: "manual"
+ });
+ return {
+ status: res.status,
+ location: res.headers.get("location") || "",
+ contentType: res.headers.get("content-type") || "",
+ contentLength: res.headers.get("content-length") || ""
+ };
+}
+
+async function main() {
+ const loginRes = await fetch("https://www.mega-debrid.eu/index.php?form=login", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded",
+ "User-Agent": "Mozilla/5.0"
+ },
+ body: new URLSearchParams({
+ login: LOGIN,
+ password: PASSWORD,
+ remember: "on"
+ }),
+ redirect: "manual"
+ });
+
+ const cookie = cookieFrom(loginRes.headers);
+ console.log("login", loginRes.status, loginRes.headers.get("location") || "");
+
+ const debridRes = await fetch("https://www.mega-debrid.eu/index.php?form=debrid", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded",
+ "User-Agent": "Mozilla/5.0",
+ Cookie: cookie,
+ Referer: "https://www.mega-debrid.eu/index.php?page=debrideur&lang=de"
+ },
+ body: new URLSearchParams({
+ links: LINKS.join("\n"),
+ password: "",
+ showLinks: "1"
+ })
+ });
+
+ const html = await debridRes.text();
+ const codes = parseDebridCodes(html);
+ console.log("codes", codes.length);
+ if (codes.length === 0) {
+ throw new Error("No processDebrid codes found");
+ }
+
+ for (let i = 0; i < Math.min(3, codes.length); i += 1) {
+ const c = codes[i];
+ const resolved = await resolveCode(cookie, c.code);
+ if (!resolved.ok) {
+ console.log(`[FAIL] code ${c.code}: ${resolved.reason}`);
+ continue;
+ }
+ console.log(`[OK] code ${c.code} -> ${resolved.link}`);
+ const probe = await probeDownload(resolved.link);
+ console.log(` probe status=${probe.status} type=${probe.contentType} len=${probe.contentLength} loc=${probe.location}`);
+ }
+}
+
+await main();
diff --git a/src/main/constants.ts b/src/main/constants.ts
index d8fada8..bc07863 100644
--- a/src/main/constants.ts
+++ b/src/main/constants.ts
@@ -3,7 +3,7 @@ import os from "node:os";
import { AppSettings } from "../shared/types";
export const APP_NAME = "Debrid Download Manager";
-export const APP_VERSION = "1.1.19";
+export const APP_VERSION = "1.1.20";
export const API_BASE_URL = "https://api.real-debrid.com/rest/1.0";
export const DCRYPT_UPLOAD_URL = "https://dcrypt.it/decrypt/upload";
diff --git a/src/main/mega-web-fallback.ts b/src/main/mega-web-fallback.ts
index 9b5fed5..2790bee 100644
--- a/src/main/mega-web-fallback.ts
+++ b/src/main/mega-web-fallback.ts
@@ -1,4 +1,3 @@
-import { BrowserWindow } from "electron";
import { UnrestrictedLink } from "./realdebrid";
import { compactErrorText, filenameFromUrl, sleep } from "./utils";
@@ -7,18 +6,83 @@ type MegaCredentials = {
password: string;
};
-type MegaWebResult = {
- directUrl: string;
- fileName: string;
+type CodeEntry = {
+ code: string;
+ linkHint: string;
};
-export class MegaWebFallback {
- private browser: BrowserWindow | null = null;
+const LOGIN_URL = "https://www.mega-debrid.eu/index.php?form=login";
+const DEBRID_URL = "https://www.mega-debrid.eu/index.php?form=debrid";
+const DEBRID_AJAX_URL = "https://www.mega-debrid.eu/index.php?ajax=debrid&json";
+const DEBRID_REFERER = "https://www.mega-debrid.eu/index.php?page=debrideur&lang=de";
+function normalizeLink(link: string): string {
+ return link.trim().toLowerCase();
+}
+
+function parseSetCookie(raw: string): string {
+ return raw
+ .split(",")
+ .map((chunk) => chunk.split(";")[0].trim())
+ .filter(Boolean)
+ .join("; ");
+}
+
+function parseCodes(html: string): CodeEntry[] {
+ const entries: CodeEntry[] = [];
+ const cardRegex = /
]*class=['"][^'"]*acp-box[^'"]*['"][^>]*>[\s\S]*?<\/div>/gi;
+ let cardMatch: RegExpExecArray | null;
+ while ((cardMatch = cardRegex.exec(html)) !== null) {
+ const block = cardMatch[0];
+ const linkTitle = (block.match(/
\s*Link:\s*([^<]+)<\/h3>/i)?.[1] || "").trim();
+ const code = block.match(/processDebrid\(\d+,'([^']+)',0\)/i)?.[1] || "";
+ if (!code) {
+ continue;
+ }
+ entries.push({ code, linkHint: normalizeLink(linkTitle) });
+ }
+
+ if (entries.length === 0) {
+ const fallbackRegex = /processDebrid\(\d+,'([^']+)',0\)/gi;
+ let m: RegExpExecArray | null;
+ while ((m = fallbackRegex.exec(html)) !== null) {
+ entries.push({ code: m[1], linkHint: "" });
+ }
+ }
+
+ return entries;
+}
+
+function pickCode(entries: CodeEntry[], link: string): string {
+ if (entries.length === 0) {
+ return "";
+ }
+ const target = normalizeLink(link);
+ const match = entries.find((entry) => entry.linkHint && entry.linkHint.includes(target));
+ return (match?.code || entries[0].code || "").trim();
+}
+
+function parseDebridJson(text: string): { link: string; text: string } | null {
+ try {
+ const parsed = JSON.parse(text) as { link?: string; text?: string };
+ return {
+ link: String(parsed.link || ""),
+ text: String(parsed.text || "")
+ };
+ } catch {
+ return null;
+ }
+}
+
+export class MegaWebFallback {
private queue: Promise = Promise.resolve();
private getCredentials: () => MegaCredentials;
+ private cookie = "";
+
+ private cookieSetAt = 0;
+
public constructor(getCredentials: () => MegaCredentials) {
this.getCredentials = getCredentials;
}
@@ -30,20 +94,29 @@ export class MegaWebFallback {
return null;
}
- const browser = await this.ensureBrowser();
- const authOk = await this.login(browser, creds.login, creds.password);
- if (!authOk) {
- throw new Error("Mega-Web-Login fehlgeschlagen");
+ if (!this.cookie || Date.now() - this.cookieSetAt > 20 * 60 * 1000) {
+ await this.login(creds.login, creds.password);
}
- const data = await this.generateLink(browser, link);
- if (!data?.directUrl) {
- throw new Error("Mega-Web konnte keinen Downloadlink erzeugen");
+ const generated = await this.generate(link);
+ if (!generated) {
+ this.cookie = "";
+ await this.login(creds.login, creds.password);
+ const retry = await this.generate(link);
+ if (!retry) {
+ return null;
+ }
+ return {
+ directUrl: retry.directUrl,
+ fileName: retry.fileName || filenameFromUrl(link),
+ fileSize: null,
+ retriesUsed: 0
+ };
}
return {
- directUrl: data.directUrl,
- fileName: data.fileName || filenameFromUrl(link),
+ directUrl: generated.directUrl,
+ fileName: generated.fileName || filenameFromUrl(link),
fileSize: null,
retriesUsed: 0
};
@@ -56,112 +129,106 @@ export class MegaWebFallback {
return run;
}
- private async ensureBrowser(): Promise {
- if (this.browser && !this.browser.isDestroyed()) {
- return this.browser;
- }
- this.browser = new BrowserWindow({
- show: false,
- webPreferences: {
- sandbox: true,
- contextIsolation: true,
- nodeIntegration: false,
- partition: "persist:mega-web"
- }
+ private async login(login: string, password: string): Promise {
+ const response = await fetch(LOGIN_URL, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded",
+ "User-Agent": "Mozilla/5.0"
+ },
+ body: new URLSearchParams({
+ login,
+ password,
+ remember: "on"
+ }),
+ redirect: "manual"
});
- return this.browser;
+
+ const cookie = parseSetCookie(response.headers.get("set-cookie") || "");
+ if (!cookie) {
+ throw new Error("Mega-Web Login liefert kein Session-Cookie");
+ }
+ this.cookie = cookie;
+ this.cookieSetAt = Date.now();
}
- private async login(browser: BrowserWindow, login: string, password: string): Promise {
- await browser.loadURL("https://www.mega-debrid.eu/index.php?page=login&lang=en");
- await sleep(600);
- const result = await browser.webContents.executeJavaScript(`(async () => {
- const hasLogout = Boolean(document.querySelector('a[href*="logout"], a[href*="debrideur"], a[href*="debrid"]'));
- if (hasLogout) return { ok: true };
- const form = document.querySelector('#formulaire_login') || document.querySelector('form[action*="form=login"]') || document.querySelector('form');
- if (!form) return { ok: false, reason: 'Login-Form nicht gefunden' };
- const loginInput = form.querySelector('input[name="login"], #user_login');
- const passInput = form.querySelector('input[name="password"], #user_password');
- if (!loginInput || !passInput) return { ok: false, reason: 'Login-Felder fehlen' };
- loginInput.value = ${JSON.stringify(login)};
- passInput.value = ${JSON.stringify(password)};
- const submit = form.querySelector('button[type="submit"], input[type="submit"], #user_submit');
- if (submit) { submit.click(); } else { form.submit(); }
- return { ok: true };
- })();`, true);
- if (!result?.ok) {
- return false;
+ private async generate(link: string): Promise<{ directUrl: string; fileName: string } | null> {
+ const page = await fetch(DEBRID_URL, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded",
+ "User-Agent": "Mozilla/5.0",
+ Cookie: this.cookie,
+ Referer: DEBRID_REFERER
+ },
+ body: new URLSearchParams({
+ links: link,
+ password: "",
+ showLinks: "1"
+ })
+ });
+
+ const html = await page.text();
+ const code = pickCode(parseCodes(html), link);
+ if (!code) {
+ return null;
}
- for (let i = 0; i < 30; i += 1) {
- await sleep(350);
- const url = browser.webContents.getURL();
- if (url.includes("page=debrideur") || url.includes("page=debrid")) {
- return true;
- }
- const logged = await browser.webContents.executeJavaScript(
- "Boolean(document.querySelector('a[href*=\"debrideur\"], a[href*=\"debrid\"], a[href*=\"logout\"]'))",
- true
- ).catch(() => false);
- if (logged) {
- return true;
- }
- }
- return false;
- }
-
- private async generateLink(browser: BrowserWindow, link: string): Promise {
- await browser.loadURL("https://www.mega-debrid.eu/index.php?page=debrideur&lang=de");
- await sleep(800);
-
- const start = await browser.webContents.executeJavaScript(`(async () => {
- const textarea = document.querySelector('textarea');
- if (!textarea) return { ok: false, reason: 'Textarea fehlt' };
- textarea.value = ${JSON.stringify(link)};
- textarea.dispatchEvent(new Event('input', { bubbles: true }));
- const controls = Array.from(document.querySelectorAll('button, input[type="submit"], a.btn'));
- const trigger = controls.find((el) => {
- const text = (el.textContent || el.value || '').toLowerCase();
- return text.includes('erzeugen') || text.includes('generate') || text.includes('générer') || text.includes('downloadlink');
+ for (let attempt = 1; attempt <= 60; attempt += 1) {
+ const res = await fetch(DEBRID_AJAX_URL, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/x-www-form-urlencoded",
+ "User-Agent": "Mozilla/5.0",
+ Cookie: this.cookie,
+ Referer: DEBRID_REFERER
+ },
+ body: new URLSearchParams({
+ code,
+ autodl: "0"
+ })
});
- if (!trigger) return { ok: false, reason: 'Generate-Button fehlt' };
- trigger.click();
- return { ok: true };
- })();`, true);
- if (!start?.ok) {
- throw new Error(start?.reason || "Mega-Web konnte Request nicht starten");
- }
+ const text = (await res.text()).trim();
+ if (text === "reload") {
+ await sleep(650);
+ continue;
+ }
+ if (text === "false") {
+ return null;
+ }
- const linkHash = link.toLowerCase();
- for (let i = 0; i < 80; i += 1) {
- await sleep(500);
- const result = await browser.webContents.executeJavaScript(`(() => {
- const cards = Array.from(document.querySelectorAll('.acp-box.card, .acp-box, .card'));
- for (const card of cards) {
- const title = (card.querySelector('.title')?.textContent || '').trim();
- const href = card.querySelector('a[href*="unrestrict.link/download/file/"]')?.getAttribute('href') || '';
- const fileName = (card.querySelector('.filename')?.textContent || '').trim();
- if (!href) continue;
- const lowTitle = title.toLowerCase();
- if (lowTitle.includes(${JSON.stringify(linkHash)}) || lowTitle.includes(${JSON.stringify(link.toLowerCase())}) || !title) {
- return { directUrl: href, fileName };
- }
+ const parsed = parseDebridJson(text);
+ if (!parsed) {
+ return null;
+ }
+
+ if (!parsed.link) {
+ if (/hoster does not respond correctly|could not be done for this moment/i.test(parsed.text || "")) {
+ await sleep(1200);
+ continue;
}
return null;
- })();`, true).catch(() => null);
- if (result?.directUrl) {
- return result;
}
+
+ const fromText = parsed.text
+ .replace(/<[^>]*>/g, " ")
+ .replace(/\s+/g, " ")
+ .trim();
+
+ const nameMatch = fromText.match(/([\w .\-\[\]\(\)]+\.(?:rar|r\d{2}|zip|7z|mkv|mp4|avi|mp3|flac))/i);
+ const fileName = (nameMatch?.[1] || filenameFromUrl(link)).trim();
+ return {
+ directUrl: parsed.link,
+ fileName
+ };
}
+
return null;
}
public dispose(): void {
- if (this.browser && !this.browser.isDestroyed()) {
- this.browser.destroy();
- }
- this.browser = null;
+ this.cookie = "";
}
}