From 5b320310d36eefeb2dff3b5334ba9fbe37868e2c Mon Sep 17 00:00:00 2001 From: Peter Meier Date: Mon, 22 Dec 2025 13:28:15 +0100 Subject: [PATCH] Add cookie input field in UI for YouTube bot detection bypass --- src/components/DownloadForm.astro | 28 +++++++++++++++++++++++++++- src/i18n/de.json | 6 ++++-- src/i18n/en.json | 6 ++++-- src/pages/api/download.ts | 31 ++++++++++++++++++++++++++++--- 4 files changed, 63 insertions(+), 8 deletions(-) diff --git a/src/components/DownloadForm.astro b/src/components/DownloadForm.astro index 0c54c6b..88855a8 100644 --- a/src/components/DownloadForm.astro +++ b/src/components/DownloadForm.astro @@ -42,6 +42,28 @@ const streamOnly = process.env.STREAM_ONLY === "true"; + +
+ +
+ {t(Astro, "downloadForm.cookiesLabel")} +
+
+
+ + +
+
+
@@ -102,8 +124,12 @@ const streamOnly = process.env.STREAM_ONLY === "true"; const urlInput = document.getElementById("url") as HTMLInputElement; const formatSelect = document.getElementById("format") as HTMLSelectElement; + const cookiesInput = document.getElementById( + "cookies" + ) as HTMLTextAreaElement; const url = urlInput?.value; const format = formatSelect?.value || "mp4"; + const cookies = cookiesInput?.value?.trim() || null; if (!url || !downloadBtn || !status || !loading) return; @@ -118,7 +144,7 @@ const streamOnly = process.env.STREAM_ONLY === "true"; "Content-Type": "application/json", "x-format": format, }, - body: JSON.stringify({ url }), + body: JSON.stringify({ url, cookies }), }); // Prüfe ob Response ein Stream ist (Content-Type ist nicht JSON) diff --git a/src/i18n/de.json b/src/i18n/de.json index 341a4dc..ef6c8f5 100644 --- a/src/i18n/de.json +++ b/src/i18n/de.json @@ -26,7 +26,10 @@ "downloadSuccessful": "Download erfolgreich!", "lastFile": "Letzte Datei:", "unknownError": "Unbekannter Fehler", - "networkError": "Netzwerkfehler" + "networkError": "Netzwerkfehler", + "cookiesLabel": "Cookies (optional - für Bot-Erkennung)", + "cookiesPlaceholder": "Cookies aus Browser-Erweiterung einfügen (Netscape-Format)", + "cookiesHelp": "Cookies helfen bei YouTube Bot-Erkennung. Exportiere sie mit einer Browser-Erweiterung wie 'Get cookies.txt LOCALLY'." }, "files": { "title": "Heruntergeladene Medien", @@ -57,4 +60,3 @@ "couldNotCreateDirectory": "Konnte Download-Verzeichnis nicht erstellen: {{dir}}. Bitte prüfe die Berechtigungen." } } - diff --git a/src/i18n/en.json b/src/i18n/en.json index c8318f0..61d44d9 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -26,7 +26,10 @@ "downloadSuccessful": "Download successful!", "lastFile": "Last file:", "unknownError": "Unknown error", - "networkError": "Network error" + "networkError": "Network error", + "cookiesLabel": "Cookies (optional - for bot detection)", + "cookiesPlaceholder": "Paste cookies from browser extension (Netscape format)", + "cookiesHelp": "Cookies help bypass YouTube bot detection. Export them using a browser extension like 'Get cookies.txt LOCALLY'." }, "files": { "title": "Downloaded Files", @@ -57,4 +60,3 @@ "couldNotCreateDirectory": "Could not create download directory: {{dir}}. Please check permissions." } } - diff --git a/src/pages/api/download.ts b/src/pages/api/download.ts index 07b4a6b..48de040 100644 --- a/src/pages/api/download.ts +++ b/src/pages/api/download.ts @@ -1,7 +1,7 @@ import type { APIRoute } from "astro"; import { getSession, isLoginEnabled } from "../../lib/session"; import { tApi } from "../../lib/i18n"; -import { mkdir, unlink, readFile, readdir } from "node:fs/promises"; +import { mkdir, unlink, readFile, readdir, writeFile } from "node:fs/promises"; import { existsSync } from "node:fs"; import path from "node:path"; import { tmpdir } from "node:os"; @@ -26,8 +26,11 @@ export const POST: APIRoute = async ({ request }) => { } } + // Cookie-Unterstützung: Temporäre Datei außerhalb des try-Blocks definieren + let tempCookiesFile: string | null = null; + try { - const { url } = await request.json(); + const { url, cookies } = await request.json(); if (!url || typeof url !== "string") { return new Response( @@ -39,6 +42,20 @@ export const POST: APIRoute = async ({ request }) => { ); } + // Cookie-Unterstützung: Wenn Cookies im Request sind, temporäre Datei erstellen + if (cookies && typeof cookies === "string" && cookies.trim()) { + try { + tempCookiesFile = path.join(tmpdir(), `cookies-${Date.now()}.txt`); + await writeFile(tempCookiesFile, cookies.trim(), "utf-8"); + } catch (error) { + console.error( + "Fehler beim Erstellen der temporären Cookie-Datei:", + error + ); + // Weiter ohne Cookies + } + } + // YouTube URL validieren if (!url.includes("youtube.com") && !url.includes("youtu.be")) { return new Response( @@ -57,7 +74,8 @@ export const POST: APIRoute = async ({ request }) => { const format = request.headers.get("x-format") || "mp4"; // Cookie-Unterstützung für YouTube Bot-Erkennung - const cookiesFile = process.env.YT_DLP_COOKIES; + // Priorität: 1. Cookies aus Request, 2. Umgebungsvariable, 3. Browser-Cookies + const cookiesFile = tempCookiesFile || process.env.YT_DLP_COOKIES; const cookiesFromBrowser = process.env.YT_DLP_COOKIES_FROM_BROWSER; // JavaScript Runtime für yt-dlp (Standard: deno) @@ -312,5 +330,12 @@ export const POST: APIRoute = async ({ request }) => { }), { status: 500, headers: { "Content-Type": "application/json" } } ); + } finally { + // Temporäre Cookie-Datei löschen falls vorhanden + if (tempCookiesFile && existsSync(tempCookiesFile)) { + await unlink(tempCookiesFile).catch((err) => { + console.error("Fehler beim Löschen der temporären Cookie-Datei:", err); + }); + } } };