Add cookie input field in UI for YouTube bot detection bypass

This commit is contained in:
Peter Meier
2025-12-22 13:28:15 +01:00
parent 1c72ac1a7b
commit 5b320310d3
4 changed files with 63 additions and 8 deletions

View File

@@ -42,6 +42,28 @@ const streamOnly = process.env.STREAM_ONLY === "true";
<option value="best">{t(Astro, "downloadForm.formatBest")}</option> <option value="best">{t(Astro, "downloadForm.formatBest")}</option>
</select> </select>
</div> </div>
<div class="collapse collapse-arrow border border-base-300 bg-base-200">
<input type="checkbox" id="cookiesToggle" />
<div class="collapse-title text-sm font-medium">
{t(Astro, "downloadForm.cookiesLabel")}
</div>
<div class="collapse-content">
<div class="form-control">
<label class="label">
<span class="label-text text-xs"
>{t(Astro, "downloadForm.cookiesHelp")}</span
>
</label>
<textarea
id="cookies"
name="cookies"
class="textarea textarea-bordered h-32 text-xs font-mono"
placeholder={t(Astro, "downloadForm.cookiesPlaceholder")}
></textarea>
</div>
</div>
</div>
</form> </form>
<div id="status" class="mt-4 hidden"></div> <div id="status" class="mt-4 hidden"></div>
@@ -102,8 +124,12 @@ const streamOnly = process.env.STREAM_ONLY === "true";
const urlInput = document.getElementById("url") as HTMLInputElement; const urlInput = document.getElementById("url") as HTMLInputElement;
const formatSelect = document.getElementById("format") as HTMLSelectElement; const formatSelect = document.getElementById("format") as HTMLSelectElement;
const cookiesInput = document.getElementById(
"cookies"
) as HTMLTextAreaElement;
const url = urlInput?.value; const url = urlInput?.value;
const format = formatSelect?.value || "mp4"; const format = formatSelect?.value || "mp4";
const cookies = cookiesInput?.value?.trim() || null;
if (!url || !downloadBtn || !status || !loading) return; if (!url || !downloadBtn || !status || !loading) return;
@@ -118,7 +144,7 @@ const streamOnly = process.env.STREAM_ONLY === "true";
"Content-Type": "application/json", "Content-Type": "application/json",
"x-format": format, "x-format": format,
}, },
body: JSON.stringify({ url }), body: JSON.stringify({ url, cookies }),
}); });
// Prüfe ob Response ein Stream ist (Content-Type ist nicht JSON) // Prüfe ob Response ein Stream ist (Content-Type ist nicht JSON)

View File

@@ -26,7 +26,10 @@
"downloadSuccessful": "Download erfolgreich!", "downloadSuccessful": "Download erfolgreich!",
"lastFile": "Letzte Datei:", "lastFile": "Letzte Datei:",
"unknownError": "Unbekannter Fehler", "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": { "files": {
"title": "Heruntergeladene Medien", "title": "Heruntergeladene Medien",
@@ -57,4 +60,3 @@
"couldNotCreateDirectory": "Konnte Download-Verzeichnis nicht erstellen: {{dir}}. Bitte prüfe die Berechtigungen." "couldNotCreateDirectory": "Konnte Download-Verzeichnis nicht erstellen: {{dir}}. Bitte prüfe die Berechtigungen."
} }
} }

View File

@@ -26,7 +26,10 @@
"downloadSuccessful": "Download successful!", "downloadSuccessful": "Download successful!",
"lastFile": "Last file:", "lastFile": "Last file:",
"unknownError": "Unknown error", "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": { "files": {
"title": "Downloaded Files", "title": "Downloaded Files",
@@ -57,4 +60,3 @@
"couldNotCreateDirectory": "Could not create download directory: {{dir}}. Please check permissions." "couldNotCreateDirectory": "Could not create download directory: {{dir}}. Please check permissions."
} }
} }

View File

@@ -1,7 +1,7 @@
import type { APIRoute } from "astro"; import type { APIRoute } from "astro";
import { getSession, isLoginEnabled } from "../../lib/session"; import { getSession, isLoginEnabled } from "../../lib/session";
import { tApi } from "../../lib/i18n"; 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 { existsSync } from "node:fs";
import path from "node:path"; import path from "node:path";
import { tmpdir } from "node:os"; 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 { try {
const { url } = await request.json(); const { url, cookies } = await request.json();
if (!url || typeof url !== "string") { if (!url || typeof url !== "string") {
return new Response( 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 // YouTube URL validieren
if (!url.includes("youtube.com") && !url.includes("youtu.be")) { if (!url.includes("youtube.com") && !url.includes("youtu.be")) {
return new Response( return new Response(
@@ -57,7 +74,8 @@ export const POST: APIRoute = async ({ request }) => {
const format = request.headers.get("x-format") || "mp4"; const format = request.headers.get("x-format") || "mp4";
// Cookie-Unterstützung für YouTube Bot-Erkennung // 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; const cookiesFromBrowser = process.env.YT_DLP_COOKIES_FROM_BROWSER;
// JavaScript Runtime für yt-dlp (Standard: deno) // 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" } } { 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);
});
}
} }
}; };