diff --git a/.env.example b/.env.example index 02f69d4..5210bd1 100644 --- a/.env.example +++ b/.env.example @@ -31,3 +31,6 @@ AUTO_PAUSE_ON_COMPLETE=0 # Medya işleme adımlarını (ffprobe/ffmpeg, thumbnail ve TMDB/TVDB metadata) devre dışı bırakır; # CPU ve disk kullanımını düşürür, ancak kapalıyken medya bilgileri eksik kalır. DISABLE_MEDIA_PROCESSING=0 +# Turkanime bölüm listesi çekme işlemlerinde ayrıntılı backend loglarını açar. +# Sorun tespiti için 1 yapın, normal kullanımda 0 bırakın. +TURKANIME_DEBUG=0 diff --git a/client/src/routes/Transfers.svelte b/client/src/routes/Transfers.svelte index b6e6c14..14bf608 100644 --- a/client/src/routes/Transfers.svelte +++ b/client/src/routes/Transfers.svelte @@ -98,8 +98,23 @@ } } + function normalizeTurkanimeUrl(value) { + if (!value || typeof value !== "string") return null; + try { + const url = new URL(value.trim()); + if (url.protocol !== "https:") return null; + const host = url.hostname.toLowerCase(); + if (!host.endsWith("turkanime.tv")) return null; + if (!url.pathname.startsWith("/anime/")) return null; + url.hash = ""; + return url.toString(); + } catch { + return null; + } + } + async function handleUrlInput() { - const input = prompt("Magnet veya YouTube URL girin:"); + const input = prompt("Magnet, YouTube veya turkanime URL girin:"); if (!input) return; if (isMagnetLink(input)) { await apiFetch("/api/transfer", { @@ -110,6 +125,23 @@ await list(); return; } + const normalizedTurkanime = normalizeTurkanimeUrl(input); + if (normalizedTurkanime) { + const resp = await apiFetch("/api/turkanime/episodes", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ url: normalizedTurkanime }) + }); + if (!resp.ok) { + const data = await resp.json().catch(() => null); + alert(data?.error || "Turkanime bölümleri alınamadı."); + return; + } + const data = await resp.json().catch(() => null); + const links = Array.isArray(data?.links) ? data.links : []; + alert(links.join("\n")); + return; + } const normalizedYoutube = normalizeYoutubeUrl(input); if (normalizedYoutube) { const resp = await apiFetch("/api/youtube/download", { @@ -126,7 +158,7 @@ return; } alert( - "Yalnızca magnet linkleri veya https://www.youtube.com/watch?v=... formatındaki YouTube URL'leri destekleniyor." + "Yalnızca magnet linkleri, YouTube veya turkanime anime URL'leri destekleniyor." ); } diff --git a/server/server.js b/server/server.js index a37bb75..5b4037e 100644 --- a/server/server.js +++ b/server/server.js @@ -91,6 +91,7 @@ const YT_ALLOWED_RESOLUTIONS = new Set([ ]); const YT_EXTRACTOR_ARGS = process.env.YT_DLP_EXTRACTOR_ARGS || null; +const TURKANIME_DEBUG = String(process.env.TURKANIME_DEBUG || "").toLowerCase() === "1"; let resolvedYtDlpBinary = null; const TMDB_API_KEY = process.env.TMDB_API_KEY; const TMDB_BASE_URL = "https://api.themoviedb.org/3"; @@ -728,6 +729,52 @@ function normalizeYoutubeWatchUrl(value) { } } +function normalizeTurkanimeUrl(value) { + if (!value || typeof value !== "string") return null; + try { + const urlObj = new URL(value.trim()); + if (urlObj.protocol !== "https:") return null; + const host = urlObj.hostname.toLowerCase(); + if (!host.endsWith("turkanime.tv")) return null; + if (!urlObj.pathname.startsWith("/anime/")) return null; + urlObj.hash = ""; + return urlObj.toString(); + } catch (err) { + return null; + } +} + +function extractTurkanimeEpisodeLinks(html) { + if (!html) return []; + const listMatch = html.match( + /]*class=["'][^"']*list[^"']*menum[^"']*["'][^>]*>([\s\S]*?)<\/ul>/i + ); + if (!listMatch) return []; + const listHtml = listMatch[1]; + const hrefs = []; + const hrefRegex = /href=["']([^"']+)["']/gi; + let match; + while ((match = hrefRegex.exec(listHtml))) { + hrefs.push(match[1]); + } + const links = hrefs + .filter((href) => href.includes("/video/")) + .map((href) => { + if (href.startsWith("//")) return `https:${href}`; + if (href.startsWith("/")) return `https://www.turkanime.tv${href}`; + return href; + }) + .filter((href) => { + try { + const urlObj = new URL(href); + return urlObj.hostname.toLowerCase().endsWith("turkanime.tv"); + } catch { + return false; + } + }); + return Array.from(new Set(links)); +} + function startYoutubeDownload(url) { const normalized = normalizeYoutubeWatchUrl(url); if (!normalized) return null; @@ -6234,6 +6281,77 @@ app.post("/api/youtube/download", requireAuth, async (req, res) => { } }); +app.post("/api/turkanime/episodes", requireAuth, async (req, res) => { + try { + const rawUrl = req.body?.url; + const normalized = normalizeTurkanimeUrl(rawUrl); + if (!normalized) { + return res.status(400).json({ + ok: false, + error: "Geçerli bir turkanime.tv anime URL'si gerekli." + }); + } + if (TURKANIME_DEBUG) { + console.log("🧪 Turkanime fetch başlıyor:", normalized); + } + const resp = await fetch(normalized, { + headers: { + "User-Agent": + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36", + Accept: "text/html" + } + }); + if (!resp.ok) { + if (TURKANIME_DEBUG) { + console.warn("🧪 Turkanime HTTP hata:", resp.status, resp.statusText); + } + return res.status(502).json({ + ok: false, + error: `Turkanime sayfası alınamadı (HTTP ${resp.status}).` + }); + } + const html = await resp.text(); + if (TURKANIME_DEBUG) { + console.log("🧪 Turkanime HTML uzunluğu:", html.length); + } + const links = extractTurkanimeEpisodeLinks(html); + if (!links.length) { + if (TURKANIME_DEBUG) { + const hasBolumler = html.includes("bolumler"); + const hasList = /class=["'][^"']*list[^"']*menum/i.test(html); + const bolumIndex = html.indexOf("bolumler"); + const snippetStart = bolumIndex > 200 ? bolumIndex - 200 : 0; + const snippet = + bolumIndex >= 0 + ? html.slice(snippetStart, bolumIndex + 400) + : html.slice(0, 400); + console.warn("🧪 Turkanime bölüm listesi bulunamadı.", { + hasBolumler, + hasList, + bolumIndex, + snippet + }); + } + return res.status(404).json({ + ok: false, + error: "Bölüm listesi bulunamadı." + }); + } + if (TURKANIME_DEBUG) { + console.log("🧪 Turkanime bölüm link sayısı:", links.length); + } + res.json({ ok: true, links }); + } catch (err) { + if (TURKANIME_DEBUG) { + console.error("🧪 Turkanime hata:", err?.message || err); + } + res.status(500).json({ + ok: false, + error: err?.message || "Turkanime verisi alınamadı." + }); + } +}); + // --- 🎫 YouTube cookies yönetimi --- app.get("/api/youtube/cookies", requireAuth, (req, res) => { try {