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( + /