diff --git a/client/src/routes/Settings.svelte b/client/src/routes/Settings.svelte index 98f788e..52e7c05 100644 --- a/client/src/routes/Settings.svelte +++ b/client/src/routes/Settings.svelte @@ -1,5 +1,79 @@
@@ -8,7 +82,74 @@

Settings

-
Ayarlar içeriği yakında.
+ +
+ {#each tabs as tab} + + {/each} +
+ + {#if activeTab === "youtube"} +
+
+
+ + YouTube Cookies +
+ {#if cookiesUpdatedAt} +
Son güncelleme: {formatDate(cookiesUpdatedAt)}
+ {/if} +
+ +
+ + + + Zararlı komut çalıştırılamaz; yalnızca düz metin cookie satırları yazılır. + Maksimum 20KB. Engellenen karakterler otomatik reddedilir. + +
+ +
+ + +
+ + {#if error} +
+ + {error} +
+ {/if} + {#if success} +
+ + {success} +
+ {/if} +
+ {:else if activeTab === "general"} +
Genel ayarlar burada yer alacak.
+ {:else if activeTab === "advanced"} +
Gelişmiş ayarlar burada yer alacak.
+ {/if}
diff --git a/server/server.js b/server/server.js index 39f7e28..9d6769b 100644 --- a/server/server.js +++ b/server/server.js @@ -72,6 +72,10 @@ const generatingThumbnails = new Set(); const INFO_FILENAME = "info.json"; const YT_ID_REGEX = /^[A-Za-z0-9_-]{11}$/; const YT_DLP_BIN = process.env.YT_DLP_BIN || null; +const YT_COOKIES_PATH = + process.env.YT_DLP_COOKIES || + process.env.YT_DLP_COOKIE_FILE || + path.join(CACHE_DIR, "yt_cookies.txt"); let resolvedYtDlpBinary = null; const TMDB_API_KEY = process.env.TMDB_API_KEY; const TMDB_BASE_URL = "https://api.themoviedb.org/3"; @@ -734,10 +738,22 @@ function appendYoutubeLog(job, line) { function launchYoutubeJob(job) { const binary = getYtDlpBinary(); - const jsRuntime = + const jsRuntimeValue = process.env.YT_DLP_JS_RUNTIME || process.env.NODE_BIN || "/usr/bin/node"; + let jsRuntimeArg = jsRuntimeValue; + if (jsRuntimeValue && jsRuntimeValue.includes("=")) { + jsRuntimeArg = jsRuntimeValue; + } else if (jsRuntimeValue && jsRuntimeValue.includes(path.sep)) { + // Eğer yol verilmişse node= formatına çevir + jsRuntimeArg = `node=${jsRuntimeValue}`; + } + + const cookieFile = + (YT_COOKIES_PATH && fs.existsSync(YT_COOKIES_PATH) && YT_COOKIES_PATH) || + null; + const args = [ "-f", "bv+ba/b", @@ -746,10 +762,13 @@ function launchYoutubeJob(job) { "jpg", "--write-info-json", "--js-runtime", - jsRuntime, + jsRuntimeArg, + ...(cookieFile && fs.existsSync(cookieFile) + ? ["--cookies", cookieFile] + : []), job.url ]; - job.debug = { binary, args, logs: [] }; + job.debug = { binary, args, logs: [], jsRuntime: jsRuntimeArg, cookies: cookieFile }; const child = spawn(binary, args, { cwd: job.savePath, env: process.env @@ -5852,6 +5871,94 @@ app.post("/api/youtube/download", requireAuth, async (req, res) => { } }); +// --- 🎫 YouTube cookies yönetimi --- +app.get("/api/youtube/cookies", requireAuth, (req, res) => { + try { + if (!YT_COOKIES_PATH || !fs.existsSync(YT_COOKIES_PATH)) { + return res.json({ hasCookies: false, cookies: null, updatedAt: null }); + } + const stat = fs.statSync(YT_COOKIES_PATH); + const size = stat.size; + if (size > 20000) { + return res.json({ hasCookies: true, cookies: "", updatedAt: stat.mtimeMs }); + } + const content = fs.readFileSync(YT_COOKIES_PATH, "utf-8"); + res.json({ hasCookies: true, cookies: content, updatedAt: stat.mtimeMs }); + } catch (err) { + console.warn("⚠️ YouTube cookies okunamadı:", err.message); + res.status(500).json({ error: "Cookies okunamadı" }); + } +}); + +app.post("/api/youtube/cookies", requireAuth, (req, res) => { + try { + let cookies = req.body?.cookies; + if (typeof cookies !== "string") { + return res.status(400).json({ error: "cookies alanı metin olmalı" }); + } + cookies = cookies.replace(/\r\n/g, "\n"); + if (cookies.length > 20000) { + return res.status(400).json({ error: "Cookie içeriği çok büyük (20KB sınırı)." }); + } + if (/[^\x09\x0a\x0d\x20-\x7e]/.test(cookies)) { + return res.status(400).json({ error: "Cookie içeriğinde desteklenmeyen karakterler var." }); + } + if (!YT_COOKIES_PATH) { + return res.status(500).json({ error: "Cookie yolu tanımlı değil." }); + } + ensureDirForFile(YT_COOKIES_PATH); + fs.writeFileSync(YT_COOKIES_PATH, cookies, "utf-8"); + res.json({ ok: true, updatedAt: Date.now() }); + } catch (err) { + console.error("❌ YouTube cookies yazılamadı:", err.message); + res.status(500).json({ error: "Cookies kaydedilemedi" }); + } +}); + +// --- 🎫 YouTube cookies yönetimi --- +app.get("/api/youtube/cookies", requireAuth, (req, res) => { + try { + if (!YT_COOKIES_PATH || !fs.existsSync(YT_COOKIES_PATH)) { + return res.json({ hasCookies: false, cookies: null, updatedAt: null }); + } + const stat = fs.statSync(YT_COOKIES_PATH); + const size = stat.size; + if (size > 20000) { + return res.json({ hasCookies: true, cookies: "", updatedAt: stat.mtimeMs }); + } + const content = fs.readFileSync(YT_COOKIES_PATH, "utf-8"); + res.json({ hasCookies: true, cookies: content, updatedAt: stat.mtimeMs }); + } catch (err) { + console.warn("⚠️ YouTube cookies okunamadı:", err.message); + res.status(500).json({ error: "Cookies okunamadı" }); + } +}); + +app.post("/api/youtube/cookies", requireAuth, (req, res) => { + try { + let cookies = req.body?.cookies; + if (typeof cookies !== "string") { + return res.status(400).json({ error: "cookies alanı metin olmalı" }); + } + cookies = cookies.replace(/\r\n/g, "\n"); + if (cookies.length > 20000) { + return res.status(400).json({ error: "Cookie içeriği çok büyük (20KB sınırı)." }); + } + if (/[^\x09\x0a\x0d\x20-\x7e]/.test(cookies)) { + return res.status(400).json({ error: "Cookie içeriğinde desteklenmeyen karakterler var." }); + } + if (!YT_COOKIES_PATH) { + return res.status(500).json({ error: "Cookie yolu tanımlı değil." }); + } + ensureDirForFile(YT_COOKIES_PATH); + fs.writeFileSync(YT_COOKIES_PATH, cookies, "utf-8"); + res.json({ ok: true, updatedAt: Date.now() }); + } catch (err) { + console.error("❌ YouTube cookies yazılamadı:", err.message); + res.status(500).json({ error: "Cookies kaydedilemedi" }); + } +}); + // --- 📺 TV dizileri listesi --- app.get("/api/tvshows", requireAuth, (req, res) => { try {