test: Test için push edildi
This commit is contained in:
698
server/server.js
698
server/server.js
@@ -8,6 +8,7 @@ import mime from "mime-types";
|
||||
import { fileURLToPath } from "url";
|
||||
import { exec, spawn } from "child_process";
|
||||
import crypto from "crypto"; // 🔒 basit token üretimi için
|
||||
import puppeteer from "puppeteer";
|
||||
import { getDiskSpace, getDownloadsSize } from "./utils/diskSpace.js";
|
||||
import { createAuth } from "./modules/auth.js";
|
||||
import { buildHealthReport, healthRouter } from "./modules/health.js";
|
||||
@@ -119,6 +120,11 @@ const TURKANIME_MAX_EPISODES =
|
||||
? Number(process.env.TURKANIME_MAX_EPISODES)
|
||||
: 500;
|
||||
const TURKANIME_DEBUG = process.env.TURKANIME_DEBUG === "1";
|
||||
const MAILRU_DEBUG = process.env.MAILRU_DEBUG === "1";
|
||||
const PUPPETEER_HEADLESS = process.env.PUPPETEER_HEADLESS !== "0";
|
||||
const PUPPETEER_TIMEOUT = Number(process.env.PUPPETEER_TIMEOUT) || 30000;
|
||||
const MAILRU_TIMEOUT = Number(process.env.MAILRU_TIMEOUT) || PUPPETEER_TIMEOUT;
|
||||
const MAILRU_TRACE = process.env.MAILRU_TRACE === "1";
|
||||
const TMDB_API_KEY = process.env.TMDB_API_KEY;
|
||||
const TMDB_BASE_URL = "https://api.themoviedb.org/3";
|
||||
const TMDB_IMG_BASE =
|
||||
@@ -216,6 +222,655 @@ function logTurkanime(message) {
|
||||
console.log(`Turkanime: ${message}`);
|
||||
}
|
||||
|
||||
function logMailru(message) {
|
||||
if (!MAILRU_DEBUG) return;
|
||||
console.log(`Mailru: ${message}`);
|
||||
}
|
||||
|
||||
function logMailruTrace(message) {
|
||||
if (!MAILRU_TRACE) return;
|
||||
console.log(`MailruTrace: ${message}`);
|
||||
}
|
||||
|
||||
function isMailruVideoUrl(url) {
|
||||
if (!url || typeof url !== "string") return false;
|
||||
if (!/mail\.ru/i.test(url)) return false;
|
||||
return /\/video\/|\/video\/meta\/|\/video\/embed\/|\/embed\/|videoapi\.my\.mail\.ru/i.test(
|
||||
url
|
||||
);
|
||||
}
|
||||
|
||||
function extractIframeSrcFromHtml(html) {
|
||||
if (!html || typeof html !== "string") return null;
|
||||
const match = html.match(/<iframe[^>]+src=["']([^"']+)["']/i);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
function normalizeProviderText(text) {
|
||||
if (!text) return "";
|
||||
return String(text).replace(/[^a-z0-9]/gi, "").toUpperCase();
|
||||
}
|
||||
|
||||
function isMailProviderText(text) {
|
||||
const compact = normalizeProviderText(text);
|
||||
if (!compact) return false;
|
||||
if (compact === "MAIL" || compact === "MAILRU") return true;
|
||||
return compact.startsWith("MAIL");
|
||||
}
|
||||
|
||||
// Puppeteer browser instance'ı yeniden kullanmak için
|
||||
let browserInstance = null;
|
||||
let browserLaunchPromise = null;
|
||||
|
||||
async function getBrowser() {
|
||||
if (browserInstance && browserInstance.isConnected()) {
|
||||
return browserInstance;
|
||||
}
|
||||
if (browserLaunchPromise) {
|
||||
return browserLaunchPromise;
|
||||
}
|
||||
browserLaunchPromise = (async () => {
|
||||
try {
|
||||
logMailru("Puppeteer başlatılıyor...");
|
||||
|
||||
// Docker container içinde sistem Chromium'unu kullan
|
||||
// Yerel geliştirme ortamında Puppeteer kendi Chromium'unu kullanır
|
||||
const isDocker = fs.existsSync('/.dockerenv');
|
||||
const launchOptions = {
|
||||
headless: PUPPETEER_HEADLESS ? "new" : false,
|
||||
args: [
|
||||
'--no-sandbox',
|
||||
'--disable-setuid-sandbox',
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-accelerated-2d-canvas',
|
||||
'--disable-gpu',
|
||||
'--disable-features=SameSiteByDefaultCookies,CookiesWithoutSameSiteMustBeSecure',
|
||||
'--autoplay-policy=no-user-gesture-required',
|
||||
'--window-size=1920x1080'
|
||||
]
|
||||
};
|
||||
|
||||
// Docker'da sistem Chromium'unu kullan
|
||||
if (isDocker || process.env.PUPPETEER_EXECUTABLE_PATH) {
|
||||
launchOptions.executablePath = process.env.PUPPETEER_EXECUTABLE_PATH || '/usr/bin/chromium';
|
||||
logMailru(`Docker tespit edildi, sistem Chromium kullanılacak: ${launchOptions.executablePath}`);
|
||||
}
|
||||
|
||||
browserInstance = await puppeteer.launch(launchOptions);
|
||||
logMailru("Puppeteer başarıyla başlatıldı");
|
||||
return browserInstance;
|
||||
} catch (err) {
|
||||
logMailru(`Puppeteer başlatılamadı: ${err.message}`);
|
||||
browserLaunchPromise = null;
|
||||
throw err;
|
||||
}
|
||||
})();
|
||||
return browserLaunchPromise;
|
||||
}
|
||||
|
||||
async function extractMailruData(turkanimeVideoUrl) {
|
||||
if (!turkanimeVideoUrl || typeof turkanimeVideoUrl !== "string") {
|
||||
throw new Error("Geçerli bir Turkanime video URL'si gerekli.");
|
||||
}
|
||||
|
||||
const url = turkanimeVideoUrl.trim();
|
||||
const urlObj = new URL(url);
|
||||
const host = urlObj.hostname.toLowerCase();
|
||||
|
||||
if (!TURKANIME_HOSTS.has(host)) {
|
||||
throw new Error("Sadece turkanime.tv domain'inden linkler destekleniyor.");
|
||||
}
|
||||
|
||||
if (!urlObj.pathname.startsWith("/video/")) {
|
||||
throw new Error("Geçersiz URL formatı. /video/ path'i gerekli.");
|
||||
}
|
||||
|
||||
logMailru(`Mail.ru URL ayıklanıyor: ${url}`);
|
||||
|
||||
let page;
|
||||
let browser;
|
||||
let requestTimeout = null;
|
||||
let cdp = null;
|
||||
|
||||
try {
|
||||
browser = await getBrowser();
|
||||
page = await browser.newPage();
|
||||
cdp = await page.target().createCDPSession();
|
||||
await cdp.send('Network.enable');
|
||||
|
||||
// User-agent ve gerekli header'lar
|
||||
await page.setUserAgent('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');
|
||||
await page.setViewport({ width: 1920, height: 1080 });
|
||||
|
||||
const findMailruInPage = async () => {
|
||||
const mailruUrlFromDom = await page.evaluate(() => {
|
||||
// 1. Tüm linkleri kontrol et
|
||||
const links = document.querySelectorAll('a');
|
||||
for (const link of links) {
|
||||
const href = link.href || '';
|
||||
if (href.includes('mail.ru')) {
|
||||
return href;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Tüm iframe'leri kontrol et
|
||||
const iframes = document.querySelectorAll('iframe');
|
||||
for (const iframe of iframes) {
|
||||
const src = iframe.src || '';
|
||||
if (src.includes('mail.ru')) {
|
||||
return src;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. window.location'ta mail.ru var mı kontrol et
|
||||
if (window.location.href.includes('mail.ru')) {
|
||||
return window.location.href;
|
||||
}
|
||||
|
||||
// 4. Tüm script etiketlerini kontrol et
|
||||
const scripts = document.querySelectorAll('script');
|
||||
for (const script of scripts) {
|
||||
const content = script.textContent || '';
|
||||
const match = content.match(/(https?:\/\/[^"'\s<]*mail\.ru\/[^"'\s<]+)/i);
|
||||
if (match) {
|
||||
return match[1];
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Sayfa içeriğinde ara (decoded URL)
|
||||
const bodyText = document.body.innerHTML || document.body.textContent || '';
|
||||
const match = bodyText.match(/(https?:\/\/[^"'\s<]*mail\.ru\/[^"'\s<]+)/i);
|
||||
if (match) {
|
||||
return match[1];
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
if (mailruUrlFromDom) return mailruUrlFromDom;
|
||||
|
||||
const pageHtml = await page.content();
|
||||
const mailruMatch = pageHtml.match(/(https?:\/\/[^"'\s<]*mail\.ru\/[^"'\s<]+)/i);
|
||||
return mailruMatch ? mailruMatch[1] : null;
|
||||
};
|
||||
|
||||
const waitForMailruMeta = async () => {
|
||||
try {
|
||||
const response = await page.waitForResponse(
|
||||
res =>
|
||||
res.url().includes('mail.ru') &&
|
||||
res.url().includes('/video/meta/'),
|
||||
{ timeout: MAILRU_TIMEOUT }
|
||||
);
|
||||
let metaJson = null;
|
||||
try {
|
||||
metaJson = await response.json();
|
||||
} catch (e) {
|
||||
const text = await response.text();
|
||||
metaJson = text;
|
||||
}
|
||||
return { url: response.url(), meta: metaJson };
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const waitMs = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
const tryClickPlay = async () => {
|
||||
try {
|
||||
await page.evaluate(() => {
|
||||
const selectors = [
|
||||
'button[aria-label*="Play"]',
|
||||
'button[title*="Play"]',
|
||||
'.vjs-big-play-button',
|
||||
'.jw-icon-playback',
|
||||
'.plyr__control',
|
||||
'.plyr__control--overlaid',
|
||||
'.vjs-big-play-button .vjs-icon-placeholder'
|
||||
];
|
||||
for (const sel of selectors) {
|
||||
const el = document.querySelector(sel);
|
||||
if (el) {
|
||||
el.click();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
} catch (e) {
|
||||
// Yoksay
|
||||
}
|
||||
};
|
||||
|
||||
// Request/Response interceptor - mail.ru URL'ini yakalamak için
|
||||
// Promise'i sayfa yüklemeden ÖNCE kur
|
||||
const mailruUrlPromise = new Promise((resolve) => {
|
||||
let resolved = false;
|
||||
|
||||
requestTimeout = setTimeout(() => {
|
||||
if (!resolved) {
|
||||
resolved = true;
|
||||
resolve(null);
|
||||
}
|
||||
}, MAILRU_TIMEOUT);
|
||||
|
||||
// Response dinleyicisi - mail.ru yanıtlarını yakala
|
||||
page.on('response', async (response) => {
|
||||
if (resolved) return;
|
||||
|
||||
try {
|
||||
const responseUrl = response.url();
|
||||
// Mail.ru video bağlantılarını yakala
|
||||
if (isMailruVideoUrl(responseUrl)) {
|
||||
resolved = true;
|
||||
clearTimeout(requestTimeout);
|
||||
logMailru(`Mail.ru URL bulundu (response): ${responseUrl}`);
|
||||
resolve(responseUrl);
|
||||
}
|
||||
if (MAILRU_TRACE && responseUrl.includes('mail.ru')) {
|
||||
logMailruTrace(`response: ${responseUrl}`);
|
||||
}
|
||||
} catch (err) {
|
||||
// Response hatası yoksay
|
||||
}
|
||||
});
|
||||
|
||||
// Request dinleyicisi - mail.ru request'lerini de yakala
|
||||
page.on('request', (request) => {
|
||||
if (resolved) return;
|
||||
|
||||
try {
|
||||
const requestUrl = request.url();
|
||||
// Mail.ru video bağlantılarını yakala
|
||||
if (isMailruVideoUrl(requestUrl)) {
|
||||
resolved = true;
|
||||
clearTimeout(requestTimeout);
|
||||
logMailru(`Mail.ru URL bulundu (request): ${requestUrl}`);
|
||||
resolve(requestUrl);
|
||||
}
|
||||
if (MAILRU_TRACE && requestUrl.includes('mail.ru')) {
|
||||
logMailruTrace(`request: ${requestUrl}`);
|
||||
}
|
||||
} catch (err) {
|
||||
// Request hatası yoksay
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const mailruMetaPromise = waitForMailruMeta();
|
||||
|
||||
const mailruCdpPromise = new Promise(resolve => {
|
||||
let done = false;
|
||||
cdp.on('Network.responseReceived', async (event) => {
|
||||
if (done) return;
|
||||
const responseUrl = event?.response?.url || '';
|
||||
if (responseUrl.includes('mail.ru') && responseUrl.includes('/video/meta/')) {
|
||||
done = true;
|
||||
try {
|
||||
const body = await cdp.send('Network.getResponseBody', {
|
||||
requestId: event.requestId
|
||||
});
|
||||
let meta = null;
|
||||
try {
|
||||
meta = JSON.parse(body.body);
|
||||
} catch {
|
||||
meta = body.body;
|
||||
}
|
||||
resolve({ url: responseUrl, meta });
|
||||
} catch (e) {
|
||||
resolve({ url: responseUrl, meta: null });
|
||||
}
|
||||
} else if (MAILRU_TRACE && responseUrl.includes('mail.ru')) {
|
||||
logMailruTrace(`cdp response: ${responseUrl}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (MAILRU_TRACE) {
|
||||
page.on('requestfailed', request => {
|
||||
const url = request.url();
|
||||
if (url.includes('mail.ru')) {
|
||||
logMailruTrace(`request failed: ${url} (${request.failure()?.errorText || 'unknown'})`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
logMailru("Sayfa yükleniyor...");
|
||||
await page.goto(url, {
|
||||
waitUntil: 'networkidle2',
|
||||
timeout: PUPPETEER_TIMEOUT
|
||||
});
|
||||
|
||||
const metaEarly = await Promise.race([mailruMetaPromise, mailruCdpPromise, waitMs(1000)]);
|
||||
if (metaEarly && metaEarly.url) {
|
||||
logMailru(`Mail.ru meta yanıtı bulundu: ${metaEarly.url}`);
|
||||
return { mailruUrl: metaEarly.url, mailruMeta: metaEarly.meta };
|
||||
}
|
||||
|
||||
logMailru("İlk DOM taraması yapılıyor...");
|
||||
const initialMailru = await findMailruInPage();
|
||||
if (initialMailru) {
|
||||
logMailru(`Mail.ru URL ilk taramada bulundu: ${initialMailru}`);
|
||||
clearTimeout(requestTimeout);
|
||||
return { mailruUrl: initialMailru, mailruMeta: null };
|
||||
}
|
||||
|
||||
logMailru("MAIL butonu aranıyor...");
|
||||
|
||||
// Tüm butonları ve linkleri kontrol et - daha güvenilir yöntem
|
||||
const buttons = await page.$$('button, a');
|
||||
let mailButton = null;
|
||||
|
||||
logMailru(`${buttons.length} adet buton bulundu, taranıyor...`);
|
||||
|
||||
const candidates = [];
|
||||
for (let i = 0; i < buttons.length; i++) {
|
||||
const btn = buttons[i];
|
||||
try {
|
||||
const text = await page.evaluate(el => (el.textContent || el.innerText || '').trim(), btn);
|
||||
const onclick = await page.evaluate(el => el.getAttribute('onclick') || '', btn);
|
||||
const onclickLower = onclick.toLowerCase();
|
||||
if (onclickLower.includes('indexicerik')) {
|
||||
candidates.push({ index: i, btn, text, onclick });
|
||||
}
|
||||
} catch (e) {
|
||||
// Devam et
|
||||
}
|
||||
}
|
||||
|
||||
for (const c of candidates) {
|
||||
if (isMailProviderText(c.text)) {
|
||||
mailButton = c.btn;
|
||||
logMailru(`MAIL butonu bulundu (index: ${c.index}): ${c.text}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mailButton && candidates.length) {
|
||||
logMailru("MAIL text eşleşmesi yok; IndexIcerik adaylarından devam edilecek.");
|
||||
}
|
||||
|
||||
const videosecResponsePromise = page
|
||||
.waitForResponse(res => res.url().includes('/ajax/videosec'), { timeout: 8000 })
|
||||
.catch(() => null);
|
||||
|
||||
if (!mailButton) {
|
||||
logMailru("MAIL butonu bulunamadı. Fallback akışları deneniyor...");
|
||||
} else {
|
||||
// Butona tıkla
|
||||
logMailru("MAIL butonuna tıklanıyor...");
|
||||
|
||||
// Önce onclick'teki URL'i al
|
||||
const onclickValue = await page.evaluate(el => el.getAttribute('onclick'), mailButton);
|
||||
logMailru(`Onclick değeri: ${onclickValue ? onclickValue.substring(0, 100) : 'yok'}`);
|
||||
|
||||
// Tıkla
|
||||
await mailButton.click();
|
||||
}
|
||||
|
||||
// Ajax sonrası iframe'in yüklenmesini bekle
|
||||
logMailru("Ajax sonrası iframe yükleniyor...");
|
||||
|
||||
// AJAX cevabını yakala ve iframe src'yi çıkar
|
||||
try {
|
||||
const videosecResponse = await videosecResponsePromise;
|
||||
if (videosecResponse) {
|
||||
const videosecHtml = await videosecResponse.text();
|
||||
const iframeSrcFromAjax = extractIframeSrcFromHtml(videosecHtml);
|
||||
if (iframeSrcFromAjax) {
|
||||
const embedUrl = iframeSrcFromAjax.startsWith('//')
|
||||
? 'https:' + iframeSrcFromAjax
|
||||
: iframeSrcFromAjax;
|
||||
logMailru(`videosec içinden embed URL bulundu: ${embedUrl.substring(0, 100)}...`);
|
||||
// iframe src'yi sayfada set ederek aynı sayfa içinde kal
|
||||
try {
|
||||
await page.waitForSelector('#videodetay iframe', { timeout: 10000 });
|
||||
await page.evaluate((src) => {
|
||||
const iframe = document.querySelector('#videodetay iframe');
|
||||
if (iframe && (!iframe.src || iframe.src === "about:blank")) {
|
||||
iframe.src = src;
|
||||
}
|
||||
}, embedUrl);
|
||||
} catch (e) {
|
||||
// yoksay
|
||||
}
|
||||
|
||||
await tryClickPlay();
|
||||
const metaAfterEmbed = await Promise.race([waitForMailruMeta(), mailruCdpPromise, waitMs(15000)]);
|
||||
if (metaAfterEmbed && metaAfterEmbed.url) {
|
||||
logMailru(`Mail.ru meta yanıtı iframe (aynı sayfa) sonrası bulundu: ${metaAfterEmbed.url}`);
|
||||
return { mailruUrl: metaAfterEmbed.url, mailruMeta: metaAfterEmbed.meta };
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// videosec bulunamazsa DOM akışı devam etsin
|
||||
}
|
||||
|
||||
// Biraz bekle - ajax'ın tamamlanması için
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
// #videodetay içindeki iframe'i bekle ve src'sini al
|
||||
try {
|
||||
if (page.url().includes('/video/')) {
|
||||
try {
|
||||
await page.waitForSelector('#videodetay iframe', { timeout: 10000 });
|
||||
logMailru("iframe bulundu!");
|
||||
} catch (e) {
|
||||
logMailru("videodetay iframe bulunamadı, devam ediliyor...");
|
||||
}
|
||||
}
|
||||
|
||||
let iframeSrc = null;
|
||||
// İframe src'sini al (video sayfası)
|
||||
iframeSrc = await page.evaluate(() => {
|
||||
const iframe = document.querySelector('#videodetay iframe');
|
||||
return iframe ? iframe.src : null;
|
||||
});
|
||||
logMailru(`iframe src: ${iframeSrc ? iframeSrc.substring(0, 100) : 'yok'}`);
|
||||
if (!iframeSrc) {
|
||||
throw new Error("iframe src bulunamadı");
|
||||
}
|
||||
|
||||
// Embed sayfası bir SPA - JavaScript ile içerik yüklüyor
|
||||
// JavaScript'in tam yüklenmesini bekle
|
||||
logMailru("Embed sayfasında JavaScript yükleniyor...");
|
||||
|
||||
// Video player'ın yüklenmesini bekle (önce iframe ara)
|
||||
let iframeElement = null;
|
||||
let videoElement = null;
|
||||
|
||||
try {
|
||||
// Önce iframe'i ara
|
||||
iframeElement = await page.waitForSelector('iframe', { timeout: 10000 });
|
||||
logMailru("İframe bulundu!");
|
||||
} catch (e) {
|
||||
logMailru("İframe timeout, video elementi aranıyor...");
|
||||
try {
|
||||
videoElement = await page.waitForSelector('video', { timeout: 5000 });
|
||||
logMailru("Video elementi bulundu!");
|
||||
try {
|
||||
await page.evaluate(el => {
|
||||
try {
|
||||
el && el.play && el.play();
|
||||
} catch (e) {
|
||||
// Yoksay
|
||||
}
|
||||
}, videoElement);
|
||||
} catch (e) {
|
||||
// Yoksay
|
||||
}
|
||||
} catch (e2) {
|
||||
logMailru("Video elementi de bulunamadı, devam ediliyor...");
|
||||
}
|
||||
}
|
||||
|
||||
// İframe varsa, içine gir
|
||||
if (iframeElement) {
|
||||
const iframeSrcInner = await page.evaluate(iframe => iframe.src, iframeElement);
|
||||
logMailru(`İçteki iframe src: ${iframeSrcInner ? iframeSrcInner.substring(0, 100) : 'yok'}`);
|
||||
|
||||
// İçteki iframe mail.ru ise, src'sini al
|
||||
if (iframeSrcInner && iframeSrcInner.includes('mail.ru')) {
|
||||
logMailru(`Mail.ru URL içteki iframe'de bulundu: ${iframeSrcInner}`);
|
||||
clearTimeout(requestTimeout);
|
||||
return { mailruUrl: iframeSrcInner, mailruMeta: null };
|
||||
}
|
||||
|
||||
const iframeContent = await iframeElement.contentFrame();
|
||||
if (iframeContent) {
|
||||
try {
|
||||
await iframeContent.evaluate(() => {
|
||||
const v = document.querySelector('video');
|
||||
if (v && v.play) {
|
||||
v.play().catch(() => {});
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
// Yoksay
|
||||
}
|
||||
|
||||
// İframe içinde mail.ru isteğini bekle
|
||||
try {
|
||||
const metaFromFrame = await Promise.race([
|
||||
page.waitForResponse(
|
||||
res =>
|
||||
res.url().includes('mail.ru') &&
|
||||
res.url().includes('/video/meta/') &&
|
||||
res.frame() === iframeContent,
|
||||
{ timeout: MAILRU_TIMEOUT }
|
||||
),
|
||||
mailruCdpPromise,
|
||||
waitMs(MAILRU_TIMEOUT)
|
||||
]);
|
||||
if (metaFromFrame && metaFromFrame.url) {
|
||||
let metaJson = null;
|
||||
try {
|
||||
if (metaFromFrame.json) {
|
||||
metaJson = await metaFromFrame.json();
|
||||
} else if (metaFromFrame.meta) {
|
||||
metaJson = metaFromFrame.meta;
|
||||
}
|
||||
} catch (e) {
|
||||
if (metaFromFrame.text) {
|
||||
metaJson = await metaFromFrame.text();
|
||||
} else {
|
||||
metaJson = metaFromFrame.meta || null;
|
||||
}
|
||||
}
|
||||
const metaUrl = metaFromFrame.url ? metaFromFrame.url() : metaFromFrame.url;
|
||||
logMailru(`Mail.ru meta yanıtı iframe içinde bulundu: ${metaUrl}`);
|
||||
return { mailruUrl: metaUrl, mailruMeta: metaJson };
|
||||
}
|
||||
} catch (e) {
|
||||
// Yoksay
|
||||
}
|
||||
|
||||
// İframe içinde mail.ru ara
|
||||
const mailruInIframe = await iframeContent.evaluate(() => {
|
||||
// Tüm linkler
|
||||
const links = document.querySelectorAll('a');
|
||||
for (const link of links) {
|
||||
if (link.href && link.href.includes('mail.ru')) {
|
||||
return link.href;
|
||||
}
|
||||
}
|
||||
// Tüm iframe'ler
|
||||
const iframes = document.querySelectorAll('iframe');
|
||||
for (const iframe of iframes) {
|
||||
if (iframe.src && iframe.src.includes('mail.ru')) {
|
||||
return iframe.src;
|
||||
}
|
||||
}
|
||||
// Body içinde ara
|
||||
const bodyText = document.body.innerHTML || document.body.textContent || '';
|
||||
const match = bodyText.match(/(https?:\/\/[^"'\s<]*mail\.ru\/[^"'\s<]+)/i);
|
||||
if (match) {
|
||||
return match[1];
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
if (mailruInIframe) {
|
||||
logMailru(`Mail.ru URL iframe içinden bulundu: ${mailruInIframe}`);
|
||||
clearTimeout(requestTimeout);
|
||||
return { mailruUrl: mailruInIframe, mailruMeta: null };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ekstra bekle - SPA için
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
|
||||
// Video oynatmayı tetikle (bazı sayfalarda meta isteği ancak etkileşim sonrası gelir)
|
||||
try {
|
||||
await tryClickPlay();
|
||||
const metaAfterPlay = await Promise.race([waitForMailruMeta(), waitMs(15000)]);
|
||||
if (metaAfterPlay && metaAfterPlay.url) {
|
||||
logMailru(`Mail.ru meta yanıtı play sonrası bulundu: ${metaAfterPlay.url}`);
|
||||
return { mailruUrl: metaAfterPlay.url, mailruMeta: metaAfterPlay.meta };
|
||||
}
|
||||
} catch (e) {
|
||||
// Yoksay
|
||||
}
|
||||
|
||||
// JavaScript ile DOM'da mail.ru linkini ara
|
||||
logMailru("DOM'da mail.ru URL'si aranıyor...");
|
||||
|
||||
const mailruUrlFromDom = await findMailruInPage();
|
||||
|
||||
if (mailruUrlFromDom) {
|
||||
logMailru(`Mail.ru URL DOM'dan bulundu: ${mailruUrlFromDom}`);
|
||||
clearTimeout(requestTimeout);
|
||||
return { mailruUrl: mailruUrlFromDom, mailruMeta: null };
|
||||
}
|
||||
|
||||
logMailru("DOM/HTML'de mail.ru bulunamadı");
|
||||
|
||||
} catch (e) {
|
||||
logMailru(`iframe işleme hatası: ${e.message}`);
|
||||
}
|
||||
|
||||
// Mail.ru URL'sini bekle (orijinal yöntem fallback)
|
||||
logMailru("Mail.ru URL'si bekleniyor (request listener)...");
|
||||
const mailruMetaResult = await Promise.race([mailruMetaPromise, mailruCdpPromise]);
|
||||
if (mailruMetaResult && mailruMetaResult.url) {
|
||||
return { mailruUrl: mailruMetaResult.url, mailruMeta: mailruMetaResult.meta };
|
||||
}
|
||||
|
||||
const mailruUrl = await mailruUrlPromise;
|
||||
return { mailruUrl, mailruMeta: null };
|
||||
|
||||
} finally {
|
||||
if (requestTimeout) {
|
||||
clearTimeout(requestTimeout);
|
||||
}
|
||||
if (page) {
|
||||
await page.close().catch(err => logMailru(`Page kapatma hatası: ${err.message}`));
|
||||
}
|
||||
if (cdp) {
|
||||
await cdp.detach().catch(() => {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Turkanime video URL'sini parse et
|
||||
function parseTurkanimeVideoUrl(rawUrl) {
|
||||
if (!rawUrl || typeof rawUrl !== "string") return null;
|
||||
try {
|
||||
const url = new URL(rawUrl.trim());
|
||||
const host = url.hostname.toLowerCase();
|
||||
if (!TURKANIME_HOSTS.has(host)) return null;
|
||||
const pathname = url.pathname.replace(/\/+$/, "");
|
||||
const match = pathname.match(/^\/video\/([^/]+)$/);
|
||||
if (!match) return null;
|
||||
const slug = match[1]?.trim();
|
||||
return slug ? `https://www.turkanime.tv/video/${slug}` : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function isTurkanimeNotFound(html) {
|
||||
if (!html) return false;
|
||||
return (
|
||||
@@ -6591,6 +7246,49 @@ app.post("/api/turkanime/episodes", requireAuth, async (req, res) => {
|
||||
res.json({ ok: true, slug, count: episodes.length, episodes });
|
||||
});
|
||||
|
||||
// --- 📧 Turkanime video sayfasından mail.ru linki ayıklama ---
|
||||
app.post("/api/turkanime/mailru", requireAuth, async (req, res) => {
|
||||
try {
|
||||
const rawUrl = req.body?.url;
|
||||
const normalizedUrl = parseTurkanimeVideoUrl(rawUrl);
|
||||
|
||||
if (!normalizedUrl) {
|
||||
return res.status(400).json({
|
||||
ok: false,
|
||||
error: "Geçerli bir Turkanime video URL'si gerekli. (örn: https://www.turkanime.tv/video/07-ghost-1-bolum)"
|
||||
});
|
||||
}
|
||||
|
||||
logMailru(`Mail.ru linki ayıklanıyor: ${normalizedUrl}`);
|
||||
|
||||
const mailruData = await extractMailruData(normalizedUrl);
|
||||
const mailruUrl = mailruData?.mailruUrl;
|
||||
|
||||
if (!mailruUrl) {
|
||||
return res.status(404).json({
|
||||
ok: false,
|
||||
error: "Mail.ru linki bulunamadı. Sayfa yapısı değişmiş olabilir."
|
||||
});
|
||||
}
|
||||
|
||||
logMailru(`Mail.ru linki başarıyla ayıklandı: ${mailruUrl}`);
|
||||
|
||||
res.json({
|
||||
ok: true,
|
||||
turkanimeUrl: normalizedUrl,
|
||||
mailruUrl: mailruUrl,
|
||||
mailruMeta: mailruData?.mailruMeta || null
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
logMailru(`Hata: ${err?.message || err}`);
|
||||
res.status(500).json({
|
||||
ok: false,
|
||||
error: err?.message || "Mail.ru linki ayıklanamadı."
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/api/youtube/download", requireAuth, async (req, res) => {
|
||||
try {
|
||||
const rawUrl = req.body?.url;
|
||||
|
||||
Reference in New Issue
Block a user